Project import
diff --git a/audio-tool/Android.mk b/audio-tool/Android.mk
new file mode 100644
index 0000000..c6106e7
--- /dev/null
+++ b/audio-tool/Android.mk
@@ -0,0 +1,108 @@
+#
+# Copyright (c) 2015 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+# This makefile uses 2 code generators:
+#
+#   1. gengetopt - this needs to be installed on the host.
+#   2. audio-tool-build-generate-wave-table - this is built as part of
+#   the project.
+#
+# This makefile is a little confusing because it must handle this.
+# For the wave tables, all the source files are properly generated in
+# the intermediates directory. However, for gengetopt, the cleanest
+# solution is to generate the files in the source directory instead of
+# the intermediates directory, because it avoids having to
+# -I. (i.e. the android top-of-tree).
+
+LIBAUDIO_TOOL_INTERMEDIATES := $(TARGET_OUT_INTERMEDIATES)/STATIC_LIBRARIES/libaudio-tool_intermediates
+AUDIO_TOOL_INTERMEDIATES := $(TARGET_OUT_INTERMEDIATES)/EXECUTABLES/audio-tool_intermediates
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := audio-tool-build-generate-wave-table
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_SRC_FILES := generate-wave-table.c
+include $(BUILD_HOST_EXECUTABLE)
+
+$(LIBAUDIO_TOOL_INTERMEDIATES):
+	mkdir -p $(LIBAUDIO_TOOL_INTERMEDIATES)
+
+$(LIBAUDIO_TOOL_INTERMEDIATES)/table_%.c: $(HOST_OUT_EXECUTABLES)/audio-tool-build-generate-wave-table | $(LIBAUDIO_TOOL_INTERMEDIATES)
+	audio-tool-build-generate-wave-table $(basename $*) 2048 S16 > $@
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libaudio-tool
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \
+	$(LOCAL_PATH) \
+	$(LIBAUDIO_TOOL_INTERMEDIATES)
+LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+	$(LIBAUDIO_TOOL_INTERMEDIATES)/table_square.c \
+	$(LIBAUDIO_TOOL_INTERMEDIATES)/table_sine.c \
+	$(LIBAUDIO_TOOL_INTERMEDIATES)/table_triangle.c \
+	$(LIBAUDIO_TOOL_INTERMEDIATES)/table_sawtooth.c
+LOCAL_SRC_FILES := \
+	tinyplay.c \
+	tinycap.c \
+	tinymix.c \
+	mixer.c \
+	pcm.c \
+	pulse-generator.c \
+	tone-generator.c \
+	oscillator-table.c \
+	save.c \
+	restore.c \
+	mixer_cache.c \
+	module.c \
+	defaults.c \
+	alsa-control.c \
+	config_cmd.c\
+	wav_chan_splitter.c \
+	rms.c
+include $(BUILD_STATIC_LIBRARY)
+
+# also generates cmdline.h
+AUDIO_TOOL_SRC_PATH := $(value LOCAL_PATH)
+$(AUDIO_TOOL_SRC_PATH)/cmdline.c: GENGETOPT_OUTPUT_DIR := $(AUDIO_TOOL_SRC_PATH)
+$(AUDIO_TOOL_SRC_PATH)/cmdline.c: ABS_PATH_TO_GGO := $(abspath $(LOCAL_PATH)/cmdline.ggo)
+$(AUDIO_TOOL_SRC_PATH)/cmdline.c: $(LOCAL_PATH)/cmdline.ggo
+	cd $(GENGETOPT_OUTPUT_DIR) && gengetopt --input=$(ABS_PATH_TO_GGO) --file-name=cmdline --unamed-opts
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := audio-tool
+LOCAL_MODULE_TAGS := debug
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \
+	$(LOCAL_PATH) \
+	$(AUDIO_TOOL_INTERMEDIATES)
+LOCAL_CFLAGS := \
+	-DVERSION_STR="\"1.1-rc1\"" \
+	-DCMDLINE_PARSER_VERSION="\"1.1-rc1\"" \
+	-DVERSION=0x0100F0
+LOCAL_ADDITIONAL_DEPENDENCIES := $(AUDIO_TOOL_SRC_PATH)/cmdline.c
+LOCAL_SRC_FILES := \
+	main.c \
+	config.c \
+	cmdline.c
+LOCAL_STATIC_LIBRARIES := libaudio-tool
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := audio-config
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_SRC_FILES := audio-config
+include $(BUILD_PREBUILT)
diff --git a/audio-tool/LICENSE b/audio-tool/LICENSE
new file mode 100644
index 0000000..e85388e
--- /dev/null
+++ b/audio-tool/LICENSE
@@ -0,0 +1,16 @@
+#                                                                                                                                                                                                                                          
+# Copyright (c) 2015 Nest Labs, Inc.                                                                                                                                                                                                       
+# All rights reserved.                                                                                                                                                                                                                     
+#                                                                                                                                                                                                                                          
+# Licensed under the Apache License, Version 2.0 (the "License");                                                                                                                                                                          
+# you may not use this file except in compliance with the License.                                                                                                                                                                         
+# You may obtain a copy of the License at                                                                                                                                                                                                  
+#                                                                                                                                                                                                                                          
+#      http://www.apache.org/licenses/LICENSE-2.0                                                                                                                                                                                          
+#                                                                                                                                                                                                                                          
+# Unless required by applicable law or agreed to in writing, software                                                                                                                                                                      
+# distributed under the License is distributed on an "AS IS" BASIS,                                                                                                                                                                        
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.                                                                                                                                                                 
+# See the License for the specific language governing permissions and                                                                                                                                                                      
+# limitations under the License.                                                                                                                                                                                                           
+#  
diff --git a/audio-tool/Makefile b/audio-tool/Makefile
new file mode 100644
index 0000000..a995dba
--- /dev/null
+++ b/audio-tool/Makefile
@@ -0,0 +1,131 @@
+INC = include
+CFLAGS = -g -O0 -I$(INC) -Wall
+LIB = libtinyalsa.a
+LINKOPTS := -static
+
+# Versions are 1.0, 1.0.1, 1.0.2, 1.1, 1.1.1, etc.
+VERSION_STR := 1.1-rc1
+# Version string bytes: 0xMMmmPP (MM=major, mm=minor, PP=patch)
+VERSION := 0x0100F0
+
+HOSTCC := gcc
+HOSTCXX := g++
+HOSTAR := ar
+HOSTLINKOPTS :=
+HOSTCFLAGS := $(HOSTLINKOPTS) $(CFLAGS)
+HOSTLDFLAGS :=
+HOSTLDLIBS :=
+HOSTGENGETOPT := gengetopt
+
+# If you set ARCH to anything, then CROSS_COMPILE will be used.
+ifneq ($(ARCH),)
+	TARGET_CROSS_COMPILE := $(CROSS_COMPILE)
+else
+	TARGET_CROSS_COMPILE :=
+endif
+TARGETCC := $(TARGET_CROSS_COMPILE)gcc
+TARGETCXX := $(TARGET_CROSS_COMPILE)g++
+TARGETAR := $(TARGET_CROSS_COMPILE)ar
+TARGETLINKOPTS := -static
+TARGETCFLAGS := $(TARGETLINKOPTS) $(CFLAGS) \
+	-DVERSION_STR="\"$(VERSION_STR)\"" \
+	-DCMDLINE_PARSER_VERSION="\"$(VERSION_STR)\"" \
+	-DVERSION=$(VERSION) \
+
+TARGETLDFLAGS :=
+TARGETLDLIBS := $(LIB) -lm -lrt
+
+TARGETS := $(LIB) \
+	audio-tool \
+
+LIB_OBJECTS = \
+	tinyplay.o \
+	tinycap.o \
+	tinymix.o \
+	mixer.o \
+	pcm.o \
+	pulse-generator.o \
+	tone-generator.o \
+	oscillator-table.o \
+	save.o \
+	restore.o \
+	mixer_cache.o \
+	module.o \
+	defaults.o \
+	alsa-control.o \
+	config_cmd.o \
+
+MODULES = \
+	card-omap-abe.o \
+
+all: $(TARGETS)
+
+audio-tool: main.o config.o cmdline.o $(LIB) $(MODULES)
+	$(TARGETCC) $(TARGETCFLAGS) $(TARGETLDFLAGS) -o $@ $^ $(TARGETLDLIBS)
+
+config.o: config.c cmdline.h
+	$(TARGETCC) $(TARGETCFLAGS) -c -o $@ $<
+
+cmdline.h: cmdline.c
+
+cmdline.c: cmdline.ggo
+	$(HOSTGENGETOPT) --input=$< --file-name=cmdline --unamed-opts
+
+tinymix: tinymix.o $(LIB)
+	$(TARGETCC) $(TARGETCFLAGS) $(TARGETLDFLAGS) -o $@ $^ $(TARGETLDLIBS)
+
+pulse-generator: pulse-generator.o $(LIB)
+	$(TARGETCC) $(TARGETCFLAGS) $(TARGETLDFLAGS) -o $@ $^ $(TARGETLDLIBS)
+
+$(LIB): $(LIB_OBJECTS)
+	$(TARGETAR) rc $@ $^
+
+.c.o:
+	$(TARGETCC) $(TARGETCFLAGS) -c $<
+
+clean: clean_tone_generator
+	-rm -f *.o $(TARGETS)
+	-rm -f cmdline.c cmdline.h
+
+card-omap45.o: card-omap45.c card-omap-common-4-5.h
+	$(TARGETCC) $(TARGETCFLAGS) -o $@ -c $<
+
+card-sdp4430.o: card-sdp4430.c card-omap-common-4-5.h
+	$(TARGETCC) $(TARGETCFLAGS) -o $@ -c $<
+
+##############################################
+##
+## TONE GENERATOR
+##
+##############################################
+
+TONEGEN_CFLAGS := $(TARGETCFLAGS)
+TONEGEN_LDFLAGS := $(LDFLAGS)
+TONEGEN_LDLIBS := $(LDLIBS) $(LIB) -lm
+
+TONEGEN_WAVE_LENGTH = 2048
+TONEGEN_WAVE_FORMAT = S16
+TONEGEN_EXE_TARGETS = generate-wave-table \
+	tone-generator
+TONEGEN_TABLE_TARGETS = table_square.c \
+	table_sine.c \
+	table_triangle.c \
+	table_sawtooth.c
+
+clean_tone_generator:
+	-rm -f $(TONEGEN_EXE_TARGETS) *.o
+	-rm -f $(TONEGEN_TABLE_TARGETS)
+
+# This program is intended as a build tool, so it's compiled for the host.
+generate-wave-table: generate-wave-table.o
+	$(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ $(HOSTLDLIBS) -lm
+
+# This program is intended as a build tool, so it's compiled for the host.
+generate-wave-table.o: generate-wave-table.c
+	$(HOSTCC) -c $(HOSTCFLAGS) $(HOSTCFLAGS) -o $@ $^
+
+table_%.c: generate-wave-table
+	./generate-wave-table $* $(TONEGEN_WAVE_LENGTH) $(TONEGEN_WAVE_FORMAT) > $@
+
+tone-generator.o: tone-generator.c $(TONEGEN_TABLE_TARGETS)
+	$(TARGETCC) -c $(TONEGEN_CPPFLAGS) $(TONEGEN_CFLAGS) -o $@ $<
diff --git a/audio-tool/README b/audio-tool/README
new file mode 100644
index 0000000..f112d13
--- /dev/null
+++ b/audio-tool/README
@@ -0,0 +1,80 @@
+OMAP audio-tool
+---------------
+
+The audio-tool is a swiss-army knife for testing and using audio on
+OMAP.  It includes the ability to play and record simple wave files,
+play test tones and impulses, and manipulate the ALSA mixer.
+
+There also includes a "legacy mode" for backwards-compatibility with
+the tinyalsa utils.
+
+MANIFEST
+--------
+
+As stated before, the goal is to have ONE binary, 'audio-tool'.  But
+for now we have:
+
+TARGET BINARIES:
+
+    audio-tool - The main audio-tool tool.
+
+HOST BINARIES:
+
+    generate-wave-tables - Generates C-source output with look-up tables
+        for various waveforms.
+
+BUILDING
+--------
+
+The Makefile builds statically linked binaries for the target
+platform.  The binaries for the host platform are usually dynamically
+linked.
+
+To build for the host PC, do this:
+
+   $ make
+
+To build for the target device, do this:
+
+   $ export CROSS_COMPILE=/path/to/arm-none-linux-gnueabi-
+   $ make ARCH=arm
+
+...and push the binaries to the device.
+
+LEGACY MODE
+-----------
+
+The audio-tool may be used as a drop-in replacement for the tinyalsa
+utils by creating symlinks to audio-tool.  In this mode, it accepts
+the exact same command-line syntax as those tools.  To use this mode,
+then you need to do this on your target device.
+
+    # which audio-tool
+    /usr/bin/audio-tool
+    # ln -s audio-tool /usr/bin/tinyplay
+    # ln -s audio-tool /usr/bin/tinycap
+    # ln -s audio-tool /usr/bin/tinymix
+
+You can then use those tools as you always have (with some of the
+enhanced parameters that the audio team has been using interally, such
+as the '-t duration' parameter.
+
+LICENSING
+---------
+
+BSD (3-clause)
+
+IDEAS AND FUTURE DIRECTIONS
+---------------------------
+
+* On unit tests, android's test tool looks for the last output
+  line instead of the OS return code for pass/fail.  Make sure
+  that we're compatible with that.
+
+* Add support for using the mmap() API in the tests and/or
+  play/cap.
+
+* Support UCM
+
+* Test multi-port configurations (e.g. play a system tone during
+  mm playback.  Capture/playback at the same time over misc. ports.
diff --git a/audio-tool/alsa-control.c b/audio-tool/alsa-control.c
new file mode 100644
index 0000000..17a7dd2
--- /dev/null
+++ b/audio-tool/alsa-control.c
@@ -0,0 +1,232 @@
+/*
+ * alsa-control.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "alsa-control.h"
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include <sound/asound.h>
+
+#define log_fmt(x) x
+#define LOGE(msg, ...) fprintf(stderr, log_fmt(msg) "\n", ##__VA_ARGS__)
+#ifdef LOG_NDEBUG
+#define LOGV(msg, ...) fprintf(stderr, log_fmt(msg) "\n", ##__VA_ARGS__)
+#else
+#define LOGV(msg, ...) ((void)0)
+#endif
+
+/* \brief Returns the path to a card's control device (/dev/snd/controlC#)
+ *
+ * \param card - The index of the sound card.
+ * \param dest - Pointer to a string buffer.
+ * \param dest_len - Maximum size of memory at dest.  Should be larger
+ * than sizeof(SND_CONTROL_TEMPLATE) by a few bytes (16 to 32 bytes).
+ *
+ * \return 0 on succes.  Negative error code on failure.
+ *
+ *   -EINVAL: The destination string was not large enough.
+ */
+static int card_control_path(int card, char *dest, size_t dest_len)
+{
+	snprintf(dest, dest_len, SND_CONTROL_TEMPLATE, card);
+	return 0;
+}
+
+/* \brief returns the maximum number of cards that could be plugged in.
+ *
+ * \return The maximum number of cards that could be plugged in.  This
+ * will be one more than the maximum index.
+ */
+int ah_card_max_count()
+{
+	/* XXX This is being set arbitrarily.  Need to ask kernel. */
+	return AH_MAX_CARD_COUNT;
+}
+
+/* \brief returns the number of cards currently registered with ALSA
+ *
+ * \return The number of cards registered with ALSA.  If there are no
+ * cards returns a zero.  Note that in some cases you may have a card
+ * number greater than the return of this function.  (E.g. if there
+ * were 4 cards and cards 1 and 2 were unplugged.
+ */
+int ah_card_count()
+{
+        DIR *dir;
+        struct dirent *de;
+        int count = 0;
+
+        dir = opendir(ALSA_DEVICE_DIRECTORY);
+        if (dir == 0)
+		return 0;
+
+        do {
+		de = readdir(dir);
+		if (!de)
+			break;
+		if (0 == strncmp(SND_CONTROL_FILE_PREFIX, de->d_name, strlen(SND_CONTROL_FILE_PREFIX)))
+			++count;
+        } while(1);
+
+	closedir(dir);
+
+	if (count > ah_card_max_count())
+		count = ah_card_max_count();
+
+        return count;
+}
+
+/* \brief Returns the name of sound card (by number)
+ *
+ * \param card - The sound card index.
+ * \param str - Pointer to location where string will be written.
+ * \param strlen - Amound of memory available at str
+ *
+ * \return 0 on success.  A negative error code on error.
+ */
+int ah_card_get_name(int card, char *str, size_t strlen)
+{
+	snd_ctl_card_info_t info;
+	int fd, rv = 0;
+
+	fd = ah_control_open(card, O_RDONLY);
+	if (fd < 0) {
+		LOGE("ah_control_open() failed");
+		rv = -errno;
+		goto end;
+	}
+
+	rv = ah_card_get_info(fd, &info);
+	if (rv) {
+		LOGE("error %d in getting card info", errno);
+		rv = -errno;
+		goto card_err;
+	}
+	LOGV("card %d info->name = %s", card, info.id);
+	strncpy(str, (char*)info.id, strlen);
+card_err:
+	ah_control_close(fd);
+end:
+        return rv;
+}
+
+/* \brief Return the index of the card with id name, if exists.
+ *
+ * \param name - name of card that is being searched for.
+ *
+ * \return - The card index, or a negative error code:
+ *
+ *    ENODEV - Device does not exits.
+ */
+int ah_card_find_by_name(const char* name)
+{
+	char cur[256] = "";
+        int cards = ah_card_count();
+        int k;
+        int match = 0;
+	LOGV("%s() looking for %s", __func__, name);
+        for (k = 0 ; k < cards ; ++k) {
+		ah_card_get_name(k, cur, sizeof(cur));
+		LOGV("Comparing to '%s'", cur);
+		if ( 0 == strcmp(cur, name) ) {
+			match = 1;
+			break;
+		}
+        }
+
+        if (match)
+		return k;
+
+        return -ENODEV;
+}
+
+/* \brief Grabs the card info structure from the kernel.
+ *
+ * \param card - The sound card index
+ * \param info - Pointer to card info structure.  The kernel's
+ * structure will be copied to this memory location.
+ *
+ * \return 0 on success.  Otherwise a negative error code.
+ * Error codes are the same as may be returned by open(2) and
+ * ioctl(2).
+ */
+int ah_card_get_info(int fd, snd_ctl_card_info_t *info)
+{
+        int e, rv = 0;
+        e = ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, info);
+        if(e)
+		rv = -errno;
+        return rv;
+}
+
+/* \brief Open up the control device for the specified card.
+ *
+ * \param card - The index of the card to use.
+ * \param mode - The mode with which to open the card.  Same as for
+ * open(2).
+ *
+ * Note: In fact, this function is really just a front from open(2).
+ *
+ * \return A file descriptor (int) for the device on success.  On
+ * error, returns the same errors as open(2).
+ */
+int ah_control_open(int card, int mode)
+{
+	char control_fn[sizeof(SND_CONTROL_TEMPLATE) + 32] = "";
+	int fd;
+	card_control_path(card, control_fn, sizeof(control_fn));
+	LOGV("Trying to open %s (card = %d)", control_fn, card);
+	fd = open(control_fn, mode);
+	return fd;
+}
+
+/* \brief Close the control device.
+ *
+ * \param fd - File descriptor to control device.
+ *
+ * \return 0 on success.  Returns same errors as close(2)
+ */
+int ah_control_close(int fd)
+{
+	return close(fd);
+}
diff --git a/audio-tool/alsa-control.h b/audio-tool/alsa-control.h
new file mode 100644
index 0000000..cae80ec
--- /dev/null
+++ b/audio-tool/alsa-control.h
@@ -0,0 +1,65 @@
+/*
+ * alsa-control.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __ALSA_CONTROL_H__
+#define __ALSA_CONTROL_H__
+
+#include "alsa-global.h"
+
+#include <string.h>
+#include <time.h>
+/*#include <linux/time.h>*/
+#include <sound/asound.h>
+
+/* Typedefs for kernel structs.  See <sound/asound.h>
+ */
+typedef struct snd_ctl_card_info snd_ctl_card_info_t;
+typedef struct snd_ctl_elem_id snd_ctl_elem_id_t;
+typedef struct snd_ctl_elem_info snd_ctl_elem_info_t;
+typedef struct snd_ctl_elem_list snd_ctl_elem_list_t;
+typedef struct snd_ctl_elem_value snd_ctl_elem_value_t;
+
+int ah_card_max_count();
+int ah_card_count();
+int ah_card_get_name(int card, char *str, size_t strlen);
+int ah_card_find_by_name(const char* name);
+
+int ah_control_open(int card, int mode);
+int ah_control_close(int fd);
+int ah_card_get_info(int fd, snd_ctl_card_info_t *info);
+
+
+#endif /* __ALSA_CONTROL_H__ */
diff --git a/audio-tool/alsa-global.h b/audio-tool/alsa-global.h
new file mode 100644
index 0000000..b156fac
--- /dev/null
+++ b/audio-tool/alsa-global.h
@@ -0,0 +1,47 @@
+/*
+ * alsa-global.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __ALSA_GLOBAL_H__
+#define __ALSA_GLOBAL_H__
+
+#define ALSA_DEVICE_DIRECTORY "/dev/snd"
+#define SND_CONTROL_FILE_PREFIX "controlC"
+#define SND_CONTROL_PREFIX  ALSA_DEVICE_DIRECTORY "/" SND_CONTROL_FILE_PREFIX
+#define SND_CONTROL_TEMPLATE  SND_CONTROL_PREFIX "%d"
+
+#define AH_MAX_CARD_COUNT 32
+
+#endif /* __ALSA_GLOBAL_H__ */
diff --git a/audio-tool/audio-config b/audio-tool/audio-config
new file mode 100755
index 0000000..016a9d5
--- /dev/null
+++ b/audio-tool/audio-config
@@ -0,0 +1,162 @@
+#!/system/bin/sh
+#
+# Copyright (c) 2015 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+set -x
+
+AUDIO_TOOL=/system/bin/audio-tool
+TINYMIX="${AUDIO_TOOL} mix"
+
+die() {
+    echo "ERROR: $@" 1>&2
+    exit 0
+}
+
+set_defaults() {
+    # QCOM has 1000 mixer controls, and they are fragile. Merely observing
+    # some of them causes crashes. Therefore we focus on a subset of controls
+    # that we manipulate.
+
+    ${TINYMIX} 'QUIN_MI2S_RX Audio Mixer MultiMedia1' 0 || die "failed to set mixer value"
+    ${TINYMIX} 'AIF1_CAP Mixer SLIM TX1' 0 || die "failed to set mixer value"
+    ${TINYMIX} 'AIF1_CAP Mixer SLIM TX2' 0 || die "failed to set mixer value"
+    ${TINYMIX} 'AIF1_CAP Mixer SLIM TX3' 0 || die "failed to set mixer value"
+    ${TINYMIX} 'AIF1_CAP Mixer SLIM TX4' 0 || die "failed to set mixer value"
+    ${TINYMIX} 'DEC1 MUX' ZERO || die "failed to set mixer value"
+    ${TINYMIX} 'DEC1 Volume' 84 || die "failed to set mixer value"
+    ${TINYMIX} 'DEC2 MUX' ZERO || die "failed to set mixer value"
+    ${TINYMIX} 'DEC3 MUX' ZERO || die "failed to set mixer value"
+    ${TINYMIX} 'DEC4 MUX' ZERO || die "failed to set mixer value"
+    ${TINYMIX} 'MultiMedia1 Mixer SLIM_0_TX' 0 || die "failed to set mixer value"
+    ${TINYMIX} 'SLIM TX1 MUX' ZERO || die "failed to set mixer value"
+    ${TINYMIX} 'SLIM TX2 MUX' ZERO || die "failed to set mixer value"
+    ${TINYMIX} 'SLIM TX3 MUX' ZERO || die "failed to set mixer value"
+    ${TINYMIX} 'SLIM TX4 MUX' ZERO || die "failed to set mixer value"
+    ${TINYMIX} 'SLIM_0_TX Channels' One || die "failed to set mixer value"
+}
+
+config_single_mic_recording() {
+    ${TINYMIX} 'MultiMedia1 Mixer SLIM_0_TX' 1 || die "failed to set mixer value"
+    ${TINYMIX} 'SLIM_0_TX Channels' One || die "failed to set mixer value"
+    ${TINYMIX} 'AIF1_CAP Mixer SLIM TX1' 1 || die "failed to set mixer value"
+    ${TINYMIX} 'SLIM TX1 MUX' DEC1 || die "failed to set mixer value"
+    ${TINYMIX} 'DEC1 MUX' DMIC1 || die "failed to set mixer value"
+    ${TINYMIX} 'DEC1 Volume' 68 || die "failed to set mixer value"
+}
+
+config_single_other_mic_recording() {
+    ${TINYMIX} 'MultiMedia1 Mixer SLIM_0_TX' 1 || die "failed to set mixer value"
+    ${TINYMIX} 'SLIM_0_TX Channels' One || die "failed to set mixer value"
+    ${TINYMIX} 'AIF1_CAP Mixer SLIM TX3' 1 || die "failed to set mixer value"
+    ${TINYMIX} 'SLIM TX3 MUX' DEC3 || die "failed to set mixer value"
+    ${TINYMIX} 'DEC3 MUX' DMIC3 || die "failed to set mixer value"
+    ${TINYMIX} 'DEC3 Volume' 68 || die "failed to set mixer value"
+}
+
+config_four_mic_recording() {
+    ${TINYMIX} 'MultiMedia1 Mixer SLIM_0_TX' 1 || die "failed to set mixer value"
+    ${TINYMIX} 'AIF1_CAP Mixer SLIM TX1' 1  || die "failed to set mixer value"
+    ${TINYMIX} 'AIF1_CAP Mixer SLIM TX2' 1  || die "failed to set mixer value"
+    ${TINYMIX} 'AIF1_CAP Mixer SLIM TX3' 1  || die "failed to set mixer value"
+    ${TINYMIX} 'AIF1_CAP Mixer SLIM TX4' 1  || die "failed to set mixer value"
+    ${TINYMIX} 'SLIM_0_TX Channels' Four  || die "failed to set mixer value"
+    ${TINYMIX} 'SLIM TX1 MUX' DEC1  || die "failed to set mixer value"
+    ${TINYMIX} 'DEC1 MUX' DMIC1 || die "failed to set mixer value"
+    ${TINYMIX} 'SLIM TX2 MUX' DEC2 || die "failed to set mixer value"
+    ${TINYMIX} 'DEC2 MUX' DMIC2 || die "failed to set mixer value"
+    ${TINYMIX} 'SLIM TX3 MUX' DEC3 || die "failed to set mixer value"
+    ${TINYMIX} 'DEC3 MUX' DMIC3 || die "failed to set mixer value"
+    ${TINYMIX} 'SLIM TX4 MUX' DEC4 || die "failed to set mixer value"
+    ${TINYMIX} 'DEC4 MUX' DMIC4 || die "failed to set mixer value"
+    if [ ! -z "$1" ]; then
+      if [ $1 -gt 83 ] && [ $1 -lt 125 ] ; then
+        ${TINYMIX} 'DEC1 Volume' $1 || die "failed to set mixer value"
+        ${TINYMIX} 'DEC2 Volume' $1 || die "failed to set mixer value"
+        ${TINYMIX} 'DEC3 Volume' $1 || die "failed to set mixer value"
+        ${TINYMIX} 'DEC4 Volume' $1 || die "failed to set mixer value"
+      fi
+    fi
+}
+
+config_speaker_playback() {
+    ${TINYMIX} 'QUIN_MI2S_RX Audio Mixer MultiMedia1' 1 || die "failed to set mixer value"
+    if [ ! -z "$1" ]; then
+      if [ $1 -gt 0 ] && [ $1 -lt 512 ] ; then
+        ${TINYMIX} 'Speaker Driver Playback Volume' $1 || die "failed to set mixer value"
+      fi
+    fi
+}
+
+config_loopback() {
+  config_four_mic_recording $1
+  config_speaker_playback $2
+}
+
+usage() {
+    cat <<EOF
+usage: audio-config <USE-CASE>
+
+Where <USE-CASE> may be one of:
+
+    default, d, dflt - set mixers to what are (normally) the boot-up
+    default values.
+
+    1mic, 1 - configure for single DMIC recording.
+
+    4mic, 4 - configure for 4x DMIC recording.
+
+    loopback, l - configure for 4x DMIC + Playback.
+
+    playback, p - configure for playback on speaker driver.
+
+EOF
+}
+
+if [ $# -eq 0 ] ; then
+    usage
+    exit 0
+fi
+
+if [ $# -gt 3 ] ; then
+    usage
+    die "Too many argument given"
+fi
+
+set_defaults
+
+case $1 in
+default|d|dflt)
+    ;;
+1mic|1)
+    config_single_mic_recording
+    ;;
+4mic|4)
+    config_four_mic_recording $2
+    ;;
+playback|p)
+    config_speaker_playback $2
+    ;;
+loopback|l)
+    config_loopback $2 $3
+    ;;
+alt)
+    config_single_other_mic_recording
+    ;;
+*)
+    usage
+    die "Invalid/unsupported argument"
+    ;;
+esac
diff --git a/audio-tool/card-omap-abe.c b/audio-tool/card-omap-abe.c
new file mode 100644
index 0000000..eb8fd85
--- /dev/null
+++ b/audio-tool/card-omap-abe.c
@@ -0,0 +1,1977 @@
+/*
+ * card-omap-common-4-5.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS 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.
+ *
+ */
+#ifndef __AUDIO_TOOL_CARD_OMAP_COMMON_4_5_H__
+#define __AUDIO_TOOL_CARD_OMAP_COMMON_4_5_H__
+#else
+#error This file should be treated as code and not as a header.
+#endif
+
+/*
+ * Code common between cards SDP4430 and OMAP45
+ *
+ * When matching ABE versions, the ABE FW version is attached.
+ *
+ * Symbols are not versioned until versioning is required.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <tinyalsa/asoundlib.h>
+
+#include "module.h"
+#include "alsa-control.h"
+#include "mixer_cache.h"
+
+static struct audio_tool_mixer_control_info g_defaults_common[] = {
+	{
+		.id = -1,
+		.name = "DL1 Equalizer",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"Flat response",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL2 Left Equalizer",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"High-pass 0dB",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL2 Right Equalizer",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"High-pass 0dB",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "Sidetone Equalizer",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"Flat response",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "AMIC Equalizer",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"High-pass 0dB",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "DMIC Equalizer",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"High-pass 0dB",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL1 Media Playback Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			118,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL1 Tones Playback Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL1 Voice Playback Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			120,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL1 Capture Playback Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "VXREC Media Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "VXREC Tones Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "VXREC Voice DL Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "VXREC Voice UL Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "AUDUL Media Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "AUDUL Tones Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "AUDUL Voice UL Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			120,
+		},
+	},
+	{
+		.id = -1,
+		.name = "AUDUL Voice DL Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "SDT UL Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			101,
+		},
+	},
+	{
+		.id = -1,
+		.name = "SDT DL Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			120,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DMIC1 UL Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 2,
+		.value.integer = {
+			120,
+			120,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DMIC2 UL Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 2,
+		.value.integer = {
+			120,
+			120,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DMIC3 UL Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 2,
+		.value.integer = {
+			120,
+			120,
+		},
+	},
+	{
+		.id = -1,
+		.name = "AMIC UL Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 2,
+		.value.integer = {
+			120,
+			120,
+		},
+	},
+	{
+		.id = -1,
+		.name = "BT UL Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 2,
+		.value.integer = {
+			120,
+			120,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL1 Mono Mixer",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "AUDUL Mono Mixer",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL1 MM_EXT Switch",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL1 BT_VX Switch",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL1 PDM Switch",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Sidetone Mixer Capture",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Sidetone Mixer Playback",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			1,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Capture Mixer Tones",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Capture Mixer Voice Playback",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Capture Mixer Voice Capture",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Capture Mixer Media Playback",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Voice Capture Mixer Tones Playback",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Voice Capture Mixer Media Playback",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Voice Capture Mixer Capture",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL1 Mixer Tones",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL1 Mixer Voice",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL1 Mixer Capture",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL1 Mixer Multimedia",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "MUX_VX1",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"None",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "MUX_VX0",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"None",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "MUX_UL11",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"None",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "MUX_UL10",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"None",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "MUX_UL07",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"None",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "MUX_UL06",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"None",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "MUX_UL05",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"None",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "MUX_UL04",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"None",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "MUX_UL03",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"None",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "MUX_UL02",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"None",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "MUX_UL01",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"None",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "MUX_UL00",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"None",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "Capture Preamplifier Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 2,
+		.value.integer = {
+			1,
+			1,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Capture Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 2,
+		.value.integer = {
+			4,
+			4,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Aux FM Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 2,
+		.value.integer = {
+			3,
+			3,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Headset Playback Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 2,
+		.value.integer = {
+			15,
+			15,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Handsfree Playback Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 2,
+		.value.integer = {
+			26,
+			26,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Earphone Playback Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			14,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Headset Power Mode",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"Low-Power",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "Earphone Playback Switch",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Headset Right Playback",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"Off",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "Headset Left Playback",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"Off",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "Handsfree Right Playback",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"HF DAC",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "Handsfree Left Playback",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"HF DAC",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "Analog Right Capture Route",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"Off",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "Analog Left Capture Route",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"Off",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "TWL6040 Power Mode",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"Low-Power",
+			"",
+			"",
+			"",
+		},
+	},
+};
+
+/* The AUX switch hasn't really been associated to an ABE FW version,
+ * so we check for it specifically.
+ */
+static struct audio_tool_mixer_control_info g_defaults_aux_switch[] = {
+	{
+		.id = -1,
+		.name = "Aux Left Playback Switch",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Aux Right Playback Switch",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+};
+
+static struct audio_tool_mixer_control_info g_defaults_0951[] = {
+	{
+		.id = -1,
+		.name = "DL2 Media Playback Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			118,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL2 Tones Playback Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL2 Voice Playback Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			120,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL2 Capture Playback Volume",
+		.type = MIXER_CTL_TYPE_INT,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL2 Mono Mixer",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL2 Mixer Tones",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL2 Mixer Voice",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL2 Mixer Capture",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "DL2 Mixer Multimedia",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			1,
+		},
+	},
+};
+
+static struct audio_tool_mixer_control_info g_defaults_0956[] = {
+	{
+		.id = -1,
+		.name = "DL1 PDM_DL2 Switch",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			1,
+		},
+	},
+	{
+		.id = -1,
+		.name = "PLL Selection",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"Low-Power",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "AUXR Playback Switch",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "AUXL Playback Switch",
+		.type = MIXER_CTL_TYPE_BOOL,
+		.num_values = 1,
+		.value.integer = {
+			0,
+		},
+	},
+	{
+		.id = -1,
+		.name = "Vibra Right Playback",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"Input FF",
+			"",
+			"",
+			"",
+		},
+	},
+	{
+		.id = -1,
+		.name = "Vibra Left Playback",
+		.type = MIXER_CTL_TYPE_ENUM,
+		.num_values = 1,
+		.value.enumerated = {
+			"Input FF",
+			"",
+			"",
+			"",
+		},
+	},
+};
+
+#define ABE_API_NULL 0L
+#define ABE_API_0951 951L
+#define ABE_API_0956 956L
+
+static int g_abe_api = ABE_API_NULL;
+
+static struct audio_tool_mixer_cache g_card_mix_defaults = {
+	.count = 0,
+	.ctrls = 0,
+};
+
+static char* g_playback_frontends[] = {
+	"Multimedia",
+	"Voice",
+	"Tones",
+	"MultimediaLP",
+	0,
+};
+
+static char* g_playback_backends[] = {
+	"Headset",
+	"Handsfree",
+	"Earpiece",
+	"Bluetooth",
+	0,
+};
+
+static char *g_capture_frontends[] = {
+	"Multimedia",
+	"Multimedia2",
+	"Voice",
+	0,
+};
+
+static char *g_capture_backends[] = {
+	"HeadsetMic",
+	"OnboardMic",
+	"Aux/FM",
+	"DMic0",
+	"DMic1",
+	"DMic2",
+	"Bluetooth",
+	"Echo",
+	"VoiceRec",
+	0,
+};
+
+struct route_setting
+{
+	char *ctl_name;
+	int intval;
+	char *strval;
+};
+
+#define RS_INT(str, val) { .ctl_name = str, .intval = val }
+#define RS_ENUM(str, val) { .ctl_name = str, .strval = val }
+#define RS_NULL { 0, 0, 0, }
+
+/***************************************************************************
+ * PLAYBACK ROUTE SETTINGS
+ ***************************************************************************
+ */
+
+/* For Frontend := { Multimedia, MultimediaLP }
+ * For Backend := { Headset, Earpiece, Bluetooth }
+ */
+static struct route_setting g_playback_multimedia_accessory_mix[] = {
+	RS_INT("Sidetone Mixer Playback", 1),
+	RS_INT("SDT DL Volume", 120),
+	RS_INT("DL1 Mixer Multimedia", 1),
+	RS_INT("DL1 Media Playback Volume", 118),
+	RS_NULL,
+};
+
+/* For Frontend := { Multimedia, MultimediaLP }
+ * For Backend := { Handsfree }
+ */
+static struct route_setting *g_playback_multimedia_handsfree_mix = 0;
+static struct route_setting g_playback_multimedia_handsfree_mix_0951[] = {
+	RS_INT("DL2 Mixer Multimedia", 1),
+	RS_INT("DL2 Media Playback Volume", 118),
+	RS_NULL,
+};
+static struct route_setting g_playback_multimedia_handsfree_mix_0956[] = {
+	RS_INT("Sidetone Mixer Playback", 1),
+	RS_INT("SDT DL Volume", 120),
+	RS_INT("DL1 Mixer Multimedia", 1),
+	RS_INT("DL1 Media Playback Volume", 118),
+	RS_NULL,
+};
+
+/* For Frontend := { Voice }
+ * For Backend := { Headset, Earpiece, Bluetooth }
+ */
+static struct route_setting g_playback_voice_accessory_mix[] = {
+	RS_INT("Sidetone Mixer Playback", 1),
+	RS_INT("SDT DL Volume", 120),
+	RS_INT("DL1 Mixer Voice", 1),
+	RS_INT("DL1 Voice Playback Volume", 118),
+	RS_NULL,
+};
+
+/* For Frontend := { Voice }
+ * For Backend := { Handsfree }
+ */
+static struct route_setting *g_playback_voice_handsfree_mix = 0;
+static struct route_setting g_playback_voice_handsfree_mix_0951[] = {
+	RS_INT("DL2 Mixer Voice", 1),
+	RS_INT("DL2 Voice Playback Volume", 118),
+	RS_NULL,
+};
+static struct route_setting g_playback_voice_handsfree_mix_0956[] = {
+	RS_INT("Sidetone Mixer Playback", 1),
+	RS_INT("SDT DL Volume", 120),
+	RS_INT("DL1 Mixer Voice", 1),
+	RS_INT("DL1 Voice Playback Volume", 118),
+	RS_NULL,
+};
+
+/* For Frontend := { Tones }
+ * For Backend := { Headset, Earpiece, Bluetooth }
+ */
+static struct route_setting g_playback_tones_accessory_mix[] = {
+	RS_INT("Sidetone Mixer Playback", 1),
+	RS_INT("SDT DL Volume", 120),
+	RS_INT("DL1 Mixer Tones", 1),
+	RS_INT("DL1 Tones Playback Volume", 118),
+	RS_NULL,
+};
+
+/* For Frontend := { Tones }
+ * For Backend := { Handsfree }
+ */
+static struct route_setting *g_playback_tones_handsfree_mix = 0;
+static struct route_setting g_playback_tones_handsfree_mix_0951[] = {
+	RS_INT("DL2 Mixer Tones", 1),
+	RS_INT("DL2 Tones Playback Volume", 118),
+	RS_NULL,
+};
+static struct route_setting g_playback_tones_handsfree_mix_0956[] = {
+	RS_INT("Sidetone Mixer Playback", 1),
+	RS_INT("SDT DL Volume", 120),
+	RS_INT("DL1 Mixer Tones", 1),
+	RS_INT("DL1 Tones Playback Volume", 118),
+	RS_NULL,
+};
+
+static struct route_setting g_playback_be_headset_mix[] = {
+	RS_INT("DL1 PDM Switch", 1),
+	RS_ENUM("Headset Left Playback", "HS DAC"),
+	RS_ENUM("Headset Right Playback", "HS DAC"),
+	RS_INT("Headset Playback Volume", 13),
+	RS_NULL,
+};
+
+static struct route_setting *g_playback_be_earpiece_mix = 0;
+static struct route_setting g_playback_be_earpiece_mix_0951[] = {
+	RS_INT("DL1 PDM Switch", 1),
+	RS_INT("Earphone Playback Switch", 1),
+	RS_INT("Earphone Playback Volume", 13),
+	RS_NULL,
+};
+static struct route_setting g_playback_be_earpiece_mix_0956[] = {
+	RS_INT("DL1 PDM Switch", 1),
+	RS_INT("Earphone Playback Switch", 1),
+	RS_INT("Earphone Playback Volume", 13),
+	RS_NULL,
+};
+
+static struct route_setting g_playback_be_bluetooth_mix[] = {
+	RS_INT("DL1 BT_VX Switch", 1),
+	RS_INT("BT UL Volume", 120),
+	RS_NULL,
+};
+
+static struct route_setting *g_playback_be_handsfree_mix = 0;
+static struct route_setting g_playback_be_handsfree_mix_0951[] = {
+	RS_ENUM("Handsfree Left Playback", "HF DAC"),
+	RS_ENUM("Handsfree Right Playback", "HF DAC"),
+	RS_INT("Handsfree Playback Volume", 23),
+	RS_NULL,
+};
+static struct route_setting g_playback_be_handsfree_mix_0956[] = {
+	RS_INT("DL1 PDM_DL2 Switch", 1),
+	RS_ENUM("Handsfree Left Playback", "HF DAC"),
+	RS_ENUM("Handsfree Right Playback", "HF DAC"),
+	RS_INT("Handsfree Playback Volume", 23),
+	RS_NULL,
+};
+
+/***************************************************************************
+ * CAPTURE ROUTE SETTINGS
+ ***************************************************************************
+ */
+
+static struct route_setting g_capture_multimedia_amic_mix[] = {
+	RS_ENUM("MUX_UL00", "AMic0"),
+	RS_ENUM("MUX_UL01", "AMic1"),
+	RS_ENUM("MUX_UL02", "None"),
+	RS_ENUM("MUX_UL03", "None"),
+	RS_ENUM("MUX_UL04", "None"),
+	RS_ENUM("MUX_UL05", "None"),
+	RS_ENUM("MUX_UL06", "None"),
+	RS_ENUM("MUX_UL07", "None"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia_dmic0_mix[] = {
+	RS_ENUM("MUX_UL00", "DMic0L"),
+	RS_ENUM("MUX_UL01", "DMic0R"),
+	RS_ENUM("MUX_UL02", "None"),
+	RS_ENUM("MUX_UL03", "None"),
+	RS_ENUM("MUX_UL04", "None"),
+	RS_ENUM("MUX_UL05", "None"),
+	RS_ENUM("MUX_UL06", "None"),
+	RS_ENUM("MUX_UL07", "None"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia_dmic1_mix[] = {
+	RS_ENUM("MUX_UL00", "DMic1L"),
+	RS_ENUM("MUX_UL01", "DMic1R"),
+	RS_ENUM("MUX_UL02", "None"),
+	RS_ENUM("MUX_UL03", "None"),
+	RS_ENUM("MUX_UL04", "None"),
+	RS_ENUM("MUX_UL05", "None"),
+	RS_ENUM("MUX_UL06", "None"),
+	RS_ENUM("MUX_UL07", "None"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia_dmic2_mix[] = {
+	RS_ENUM("MUX_UL00", "DMic2L"),
+	RS_ENUM("MUX_UL01", "DMic2R"),
+	RS_ENUM("MUX_UL02", "None"),
+	RS_ENUM("MUX_UL03", "None"),
+	RS_ENUM("MUX_UL04", "None"),
+	RS_ENUM("MUX_UL05", "None"),
+	RS_ENUM("MUX_UL06", "None"),
+	RS_ENUM("MUX_UL07", "None"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia_bluetooth_mix[] = {
+	RS_ENUM("MUX_UL00", "BT Left"),
+	RS_ENUM("MUX_UL01", "BT Right"),
+	RS_ENUM("MUX_UL02", "None"),
+	RS_ENUM("MUX_UL03", "None"),
+	RS_ENUM("MUX_UL04", "None"),
+	RS_ENUM("MUX_UL05", "None"),
+	RS_ENUM("MUX_UL06", "None"),
+	RS_ENUM("MUX_UL07", "None"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia_echo_mix[] = {
+	RS_ENUM("MUX_UL00", "Echo Left"),
+	RS_ENUM("MUX_UL01", "Echo Right"),
+	RS_ENUM("MUX_UL02", "None"),
+	RS_ENUM("MUX_UL03", "None"),
+	RS_ENUM("MUX_UL04", "None"),
+	RS_ENUM("MUX_UL05", "None"),
+	RS_ENUM("MUX_UL06", "None"),
+	RS_ENUM("MUX_UL07", "None"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia_vxrec_mix[] = {
+	RS_ENUM("MUX_UL00", "VX Left"),
+	RS_ENUM("MUX_UL01", "VX Right"),
+	RS_ENUM("MUX_UL02", "None"),
+	RS_ENUM("MUX_UL03", "None"),
+	RS_ENUM("MUX_UL04", "None"),
+	RS_ENUM("MUX_UL05", "None"),
+	RS_ENUM("MUX_UL06", "None"),
+	RS_ENUM("MUX_UL07", "None"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia2_amic_mix[] = {
+	RS_ENUM("MUX_UL10", "AMic0"),
+	RS_ENUM("MUX_UL11", "AMic1"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia2_dmic0_mix[] = {
+	RS_ENUM("MUX_UL10", "DMic0L"),
+	RS_ENUM("MUX_UL11", "DMic0R"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia2_dmic1_mix[] = {
+	RS_ENUM("MUX_UL10", "DMic1L"),
+	RS_ENUM("MUX_UL11", "DMic1R"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia2_dmic2_mix[] = {
+	RS_ENUM("MUX_UL10", "DMic2L"),
+	RS_ENUM("MUX_UL11", "DMic2R"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia2_bluetooth_mix[] = {
+	RS_ENUM("MUX_UL10", "BT Left"),
+	RS_ENUM("MUX_UL11", "BT Right"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia2_echo_mix[] = {
+	RS_ENUM("MUX_UL10", "Echo Left"),
+	RS_ENUM("MUX_UL11", "Echo Right"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia2_vxrec_mix[] = {
+	RS_ENUM("MUX_UL10", "VX Left"),
+	RS_ENUM("MUX_UL11", "VX Right"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_voice_amic_mix[] = {
+	RS_INT("Voice Capture Mixer Capture", 1),
+	RS_ENUM("MUX_VX0", "AMic0"),
+	RS_ENUM("MUX_VX1", "AMic1"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_voice_dmic0_mix[] = {
+	RS_INT("Voice Capture Mixer Capture", 1),
+	RS_ENUM("MUX_VX0", "DMic0L"),
+	RS_ENUM("MUX_VX1", "DMic0R"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_voice_dmic1_mix[] = {
+	RS_INT("Voice Capture Mixer Capture", 1),
+	RS_ENUM("MUX_VX0", "DMic1L"),
+	RS_ENUM("MUX_VX1", "DMic1R"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_voice_dmic2_mix[] = {
+	RS_INT("Voice Capture Mixer Capture", 1),
+	RS_ENUM("MUX_VX0", "DMic2L"),
+	RS_ENUM("MUX_VX1", "DMic2R"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_voice_bluetooth_mix[] = {
+	RS_INT("Voice Capture Mixer Capture", 1),
+	RS_ENUM("MUX_VX0", "BT Left"),
+	RS_ENUM("MUX_VX1", "BT Right"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_voice_echo_mix[] = {
+	RS_INT("Voice Capture Mixer Capture", 1),
+	RS_ENUM("MUX_VX0", "Echo Left"),
+	RS_ENUM("MUX_VX1", "Echo Right"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_voice_vxrec_mix[] = {
+	RS_INT("Voice Capture Mixer Capture", 1),
+	RS_ENUM("MUX_VX0", "VX Left"),
+	RS_ENUM("MUX_VX1", "VX Right"),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_be_headsetmic_mix[] = {
+	RS_INT("AMIC UL Volume", 120),
+	RS_ENUM("Analog Left Capture Route", "Headset Mic"),
+	RS_ENUM("Analog Right Capture Route", "Headset Mic"),
+	RS_INT("Capture Preamplifier Volume", 1),
+	RS_INT("Capture Volume", 4),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_be_onboardmic_mix[] = {
+	RS_INT("AMIC UL Volume", 120),
+	RS_ENUM("Analog Left Capture Route", "Main Mic"),
+	RS_ENUM("Analog Right Capture Route", "Sub Mic"),
+	RS_INT("Capture Preamplifier Volume", 1),
+	RS_INT("Capture Volume", 4),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_be_auxfm_mix[] = {
+	RS_INT("AMIC UL Volume", 120),
+	RS_ENUM("Analog Left Capture Route", "Aux/FM Left"),
+	RS_ENUM("Analog Right Capture Route", "Aux/FM Right"),
+	RS_INT("Capture Preamplifier Volume", 1),
+	RS_INT("Capture Volume", 4),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_be_dmic0_mix[] = {
+	RS_INT("DMIC1 UL Volume", 140),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_be_dmic1_mix[] = {
+	RS_INT("DMIC2 UL Volume", 140),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_be_dmic2_mix[] = {
+	RS_INT("DMIC3 UL Volume", 140),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_be_bluetooth_mix[] = {
+	RS_INT("BT UL Volume", 120),
+	RS_INT("DL1 BT_VX Switch", 1),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_be_echo_mix[] = {
+	RS_INT("Echo Mixer DL1", 1),
+	RS_INT("Echo Mixer DL2", 1),
+	RS_INT("Echo DL1 Volume", 120),
+	RS_INT("Echo DL2 Volume", 120),
+	RS_NULL,
+};
+
+static struct route_setting g_capture_be_vxrec_mix[] = {
+	RS_INT("Capture Mixer Tones", 1),
+	RS_INT("Capture Mixer Voice Playback", 1),
+	RS_INT("Capture Mixer Voice Capture", 1),
+	RS_INT("Capture Mixer Media Playback", 1),
+	RS_INT("VXREC Media Volume", 120),
+	RS_INT("VXREC Tones Volume", 120),
+	RS_INT("VXREC Voice DL Volume", 120),
+	RS_INT("VXREC Voice UL Volume", 120),
+	RS_NULL,
+};
+
+/* side effects: initializes g_abe_api and g_card_mix_defaults */
+static int detect_abe_api(struct audio_tool_mixer_cache *cache)
+{
+	int abe_api = ABE_API_0951;
+	int has_aux_switch = 0;
+	int count = 0;
+	int n;
+
+	/* Detect if using 09.56 API */
+	for (n = 0 ; n < cache->count ; ++n) {
+		if (0 == strcmp("DL1 PDM_DL2 Switch", cache->ctrls[n].name)) {
+			abe_api = ABE_API_0956;
+			break;
+		}
+	}
+
+	/* Detect if have aux switch */
+	for (n = 0 ; n < cache->count ; ++n) {
+		if (0 == strcmp("Aux Left Playback Switch", cache->ctrls[n].name)) {
+			has_aux_switch = 1;
+			break;
+		}
+	}
+
+	count = sizeof(g_defaults_common) / sizeof(g_defaults_common[0]);
+	if (has_aux_switch) {
+		count += sizeof(g_defaults_aux_switch) / sizeof(g_defaults_aux_switch[0]);
+	}
+	switch (abe_api) {
+	case ABE_API_0951:
+		count += sizeof(g_defaults_0951) / sizeof(g_defaults_0951[0]);
+		break;
+	case ABE_API_0956:
+		count += sizeof(g_defaults_0956) / sizeof(g_defaults_0956[0]);
+		break;
+	default:
+		assert(0);
+	}
+
+	g_card_mix_defaults.ctrls = calloc(count, sizeof(struct audio_tool_mixer_control_info));
+	memcpy(g_card_mix_defaults.ctrls, &g_defaults_common, sizeof(g_defaults_common));
+	n = sizeof(g_defaults_common) / sizeof(g_defaults_common[0]);
+	if (has_aux_switch) {
+		memcpy(g_card_mix_defaults.ctrls + n, &g_defaults_aux_switch,
+		       sizeof(g_defaults_aux_switch));
+		n += sizeof(g_defaults_aux_switch) / sizeof(g_defaults_aux_switch[0]);
+	}
+	switch (abe_api) {
+	case ABE_API_0951:
+		memcpy(g_card_mix_defaults.ctrls + n, &g_defaults_0951, sizeof(g_defaults_0951));
+		break;
+	case ABE_API_0956:
+		memcpy(g_card_mix_defaults.ctrls + n, &g_defaults_0956, sizeof(g_defaults_0956));
+		break;
+	default:
+		assert(0);
+	}
+
+	g_card_mix_defaults.count = count;
+	g_abe_api = abe_api;
+
+
+	/* initialize static id's */
+	for (n = 0 ; n < g_card_mix_defaults.count ; ++n) {
+		g_card_mix_defaults.ctrls[n].id = n;
+	}
+
+	/* Set up the routes */
+	switch (abe_api) {
+	case ABE_API_0951:
+		g_playback_multimedia_handsfree_mix = g_playback_multimedia_handsfree_mix_0951;
+		g_playback_voice_handsfree_mix = g_playback_voice_handsfree_mix_0951;
+		g_playback_tones_handsfree_mix = g_playback_tones_handsfree_mix_0951;
+		g_playback_be_earpiece_mix = g_playback_be_earpiece_mix_0951;
+		g_playback_be_handsfree_mix = g_playback_be_handsfree_mix_0951;
+		break;
+	case ABE_API_0956:
+		g_playback_multimedia_handsfree_mix = g_playback_multimedia_handsfree_mix_0956;
+		g_playback_voice_handsfree_mix = g_playback_voice_handsfree_mix_0956;
+		g_playback_tones_handsfree_mix = g_playback_tones_handsfree_mix_0956;
+		g_playback_be_earpiece_mix = g_playback_be_earpiece_mix_0956;
+		g_playback_be_handsfree_mix = g_playback_be_handsfree_mix_0956;
+		break;
+	default:
+		assert(0);
+	}
+
+	return 0;
+}
+
+static int get_mixer_defaults(struct audio_tool_mixer_cache *cache)
+{
+	struct audio_tool_mixer_cache *defs = &g_card_mix_defaults;
+	int m, n;
+	int ret = 0;
+
+	if (g_abe_api == ABE_API_NULL)
+		detect_abe_api(cache);
+
+	mixer_cache_reset_touch(defs);
+	mixer_cache_reset_touch(cache);
+
+	for (m = 0 ; m < cache->count ; ++m) {
+		n = mixer_cache_get_id_by_name(defs,
+			cache->ctrls[m].name);
+		if (n < 0) {
+			fprintf(stderr, "Warning: No default defined for %s\n", cache->ctrls[m].name);
+			ret = 1;
+			continue;
+		}
+		if (cache->ctrls[m].type != defs->ctrls[n].type) {
+			fprintf(stderr, "Warning: type mismatch on %s\n", cache->ctrls[m].name);
+			ret = 1;
+			continue;
+		}
+		memcpy(&cache->ctrls[m].value, &defs->ctrls[n].value,
+		       sizeof(g_defaults_common[0].value));
+
+		mixer_cache_touch(cache, m);
+		mixer_cache_touch(defs, n);
+	}
+
+	ret = mixer_cache_audit_touch(cache, 1) ? 1 : ret;
+	ret = mixer_cache_audit_touch(defs, 1) ? 1 : ret;
+
+	return ret;
+}
+
+static int set_route_by_array(struct mixer *mixer, struct route_setting *route,
+                              int enable)
+{
+    struct mixer_ctl *ctl;
+    unsigned int i, j;
+
+    /* Go through the route array and set each value */
+    i = 0;
+    while (route[i].ctl_name) {
+        ctl = mixer_get_ctl_by_name(mixer, route[i].ctl_name);
+        if (!ctl)
+            return -EINVAL;
+
+        if (route[i].strval) {
+            if (enable)
+                mixer_ctl_set_enum_by_string(ctl, route[i].strval);
+            else
+                mixer_ctl_set_enum_by_string(ctl, "Off");
+        } else {
+            /* This ensures multiple (i.e. stereo) values are set jointly */
+            for (j = 0; j < mixer_ctl_get_num_values(ctl); j++) {
+                if (enable)
+                    mixer_ctl_set_value(ctl, j, route[i].intval);
+                else
+                    mixer_ctl_set_value(ctl, j, 0);
+            }
+        }
+        i++;
+    }
+
+    return 0;
+}
+
+static int get_fe_be_names(int direction, char ***fes, char ***bes)
+{
+	if (direction == AUDIO_DIRECTION_PLAYBACK) {
+		*fes = g_playback_frontends;
+		*bes = g_playback_backends;
+	} else {
+		assert(direction == AUDIO_DIRECTION_CAPTURE);
+		*fes = g_capture_frontends;
+		*bes = g_capture_backends;
+	}
+	return 0;
+}
+
+#define FE_P_MM 0
+#define FE_P_VX 1
+#define FE_P_TONES 2
+#define FE_P_MMLP 3
+
+#define FE_C_MM 0
+#define FE_C_MM2 1
+#define FE_C_VX 2
+
+#define BE_P_HS 0
+#define BE_P_HF 1
+#define BE_P_EP 3
+#define BE_P_BT 4
+
+#define BE_C_HSMIC 0
+#define BE_C_OBMIC 1
+#define BE_C_AUXFM 2
+#define BE_C_DMIC0 3
+#define BE_C_DMIC1 4
+#define BE_C_DMIC2 5
+#define BE_C_BT 6
+#define BE_C_ECHO 7
+#define BE_C_VXREC 8
+
+static int config_playback(struct mixer *mixer, const char* fe,
+	const char* be, int enable, int *optional_port)
+{
+	int f, b;
+	int ret = 0;
+	int port;
+
+	if (0 == strcmp(fe, "Multimedia")) {
+		f = FE_P_MM;
+		port = 0;
+	} else if (0 == strcmp(fe, "Voice")) {
+		f = FE_P_VX;
+		port = 2;
+	} else if (0 == strcmp(fe, "Tones")) {
+		f = FE_P_TONES;
+		port = 3;
+	} else if (0 == strcmp(fe, "MultimediaLP")) {
+		f = FE_P_MMLP;
+		port = 6;
+	} else {
+		return EINVAL;
+	}
+
+	if (optional_port)
+		*optional_port = port;
+
+	if (0 == strcmp(be, "Headset")) {
+		b = BE_P_HS;
+	} else if (0 == strcmp(be, "Handsfree")) {
+		b = BE_P_HF;
+	} else if (0 == strcmp(be, "Earpiece")) {
+		b = BE_P_EP;
+	} else if (0 == strcmp(be, "Bluetooth")) {
+		b = BE_P_BT;
+	} else {
+		return EINVAL;
+	}
+
+	switch (f) {
+	case FE_P_MM:
+	case FE_P_MMLP:
+		if (b == BE_P_HF) {
+			ret = set_route_by_array(mixer,
+					g_playback_multimedia_handsfree_mix, enable);
+		} else {
+			ret = set_route_by_array(mixer,
+					g_playback_multimedia_accessory_mix, enable);
+		}
+		break;
+	case FE_P_VX:
+		if (b == BE_P_HF) {
+			ret = set_route_by_array(mixer,
+					g_playback_voice_handsfree_mix, enable);
+		} else {
+			ret = set_route_by_array(mixer,
+					g_playback_voice_accessory_mix, enable);
+		}
+		break;
+	case FE_P_TONES:
+		if (b == BE_P_HF) {
+			ret = set_route_by_array(mixer,
+					g_playback_tones_handsfree_mix, enable);
+		} else {
+			ret = set_route_by_array(mixer,
+					g_playback_tones_accessory_mix, enable);
+		}
+		break;
+	default:
+		assert(0);
+	}
+
+	if (ret)
+		return ret;
+
+	switch (b) {
+	case BE_P_HS:
+		ret = set_route_by_array(mixer,
+				g_playback_be_headset_mix, enable);
+		break;
+	case BE_P_HF:
+		ret = set_route_by_array(mixer,
+				g_playback_be_handsfree_mix, enable);
+		break;
+	case BE_P_EP:
+		ret = set_route_by_array(mixer,
+				g_playback_be_earpiece_mix, enable);
+		break;
+	case BE_P_BT:
+		ret = set_route_by_array(mixer,
+				g_playback_be_bluetooth_mix, enable);
+		break;
+	default:
+		assert(0);
+	}
+
+	return ret;
+}
+
+static int config_capture(struct mixer *mixer, const char* fe,
+	const char* be, int enable, int *optional_port)
+{
+	int f, b;
+	int ret = 0;
+	int port;
+
+	if (0 == strcmp(fe, "Multimedia")) {
+		f = FE_C_MM;
+		port = 0;
+	} else if (0 == strcmp(fe, "Multimedia2")) {
+		f = FE_C_MM2;
+		port = 1;
+	} else if (0 == strcmp(fe, "Voice")) {
+		f = FE_C_VX;
+		port = 2;
+	} else {
+		return EINVAL;
+	}
+
+	if (optional_port)
+		*optional_port = port;
+
+	if (0 == strcmp(be, "HeadsetMic")) {
+		b = BE_C_HSMIC;
+	} else if (0 == strcmp(be, "OnboardMic")) {
+		b = BE_C_OBMIC;
+	} else if (0 == strcmp(be, "Aux/FM")) {
+		b = BE_C_AUXFM;
+	} else if (0 == strcmp(be, "DMic0")) {
+		b = BE_C_DMIC0;
+	} else if (0 == strcmp(be, "DMic1")) {
+		b = BE_C_DMIC1;
+	} else if (0 == strcmp(be, "DMic2")) {
+		b = BE_C_DMIC2;
+	} else if (0 == strcmp(be, "Bluetooth")) {
+		b = BE_C_BT;
+	} else if (0 == strcmp(be, "Echo")) {
+		b = BE_C_ECHO;
+	} else if (0 == strcmp(be, "VoiceRec")) {
+		b = BE_C_VXREC;
+	} else {
+		return EINVAL;
+	}
+
+	switch (f) {
+	case FE_C_MM:
+		switch (b) {
+		case BE_C_HSMIC:
+		case BE_C_OBMIC:
+		case BE_C_AUXFM:
+			ret = set_route_by_array(mixer,
+					g_capture_multimedia_amic_mix, enable);
+			break;
+		case BE_C_DMIC0:
+			ret = set_route_by_array(mixer,
+					 g_capture_multimedia_dmic0_mix, enable);
+			break;
+		case BE_C_DMIC1:
+			ret = set_route_by_array(mixer,
+					 g_capture_multimedia_dmic1_mix, enable);
+			break;
+		case BE_C_DMIC2:
+			ret = set_route_by_array(mixer,
+					 g_capture_multimedia_dmic2_mix, enable);
+			break;
+		case BE_C_BT:
+			ret = set_route_by_array(mixer,
+					 g_capture_multimedia_bluetooth_mix, enable);
+			break;
+		case BE_C_ECHO:
+			ret = set_route_by_array(mixer,
+					g_capture_multimedia_echo_mix, enable);
+			break;
+		case BE_C_VXREC:
+			ret = set_route_by_array(mixer,
+					g_capture_multimedia_vxrec_mix, enable);
+			break;
+		}
+		break;
+	case FE_C_MM2:
+		switch (b) {
+		case BE_C_HSMIC:
+		case BE_C_OBMIC:
+		case BE_C_AUXFM:
+			ret = set_route_by_array(mixer,
+					 g_capture_multimedia2_amic_mix, enable);
+			break;
+		case BE_C_DMIC0:
+			ret = set_route_by_array(mixer,
+					 g_capture_multimedia2_dmic0_mix, enable);
+			break;
+		case BE_C_DMIC1:
+			ret = set_route_by_array(mixer,
+					 g_capture_multimedia2_dmic1_mix, enable);
+			break;
+		case BE_C_DMIC2:
+			ret = set_route_by_array(mixer,
+					 g_capture_multimedia2_dmic2_mix, enable);
+			break;
+		case BE_C_BT:
+			ret = set_route_by_array(mixer,
+					 g_capture_multimedia2_bluetooth_mix, enable);
+			break;
+		case BE_C_ECHO:
+			ret = set_route_by_array(mixer,
+					g_capture_multimedia2_echo_mix, enable);
+			break;
+		case BE_C_VXREC:
+			ret = set_route_by_array(mixer,
+					g_capture_multimedia2_vxrec_mix, enable);
+			break;
+		}
+		break;
+	case FE_C_VX:
+		switch (b) {
+		case BE_C_HSMIC:
+		case BE_C_OBMIC:
+		case BE_C_AUXFM:
+			ret = set_route_by_array(mixer,
+					 g_capture_voice_amic_mix, enable);
+			break;
+		case BE_C_DMIC0:
+			ret = set_route_by_array(mixer,
+					 g_capture_voice_dmic0_mix, enable);
+			break;
+		case BE_C_DMIC1:
+			ret = set_route_by_array(mixer,
+					 g_capture_voice_dmic1_mix, enable);
+			break;
+		case BE_C_DMIC2:
+			ret = set_route_by_array(mixer,
+					 g_capture_voice_dmic2_mix, enable);
+			break;
+		case BE_C_BT:
+			ret = set_route_by_array(mixer,
+					 g_capture_voice_bluetooth_mix, enable);
+			break;
+		case BE_C_ECHO:
+			ret = set_route_by_array(mixer,
+					g_capture_voice_echo_mix, enable);
+			break;
+		case BE_C_VXREC:
+			ret = set_route_by_array(mixer,
+					g_capture_voice_vxrec_mix, enable);
+			break;
+		}
+		break;
+	default:
+		assert(0);
+	}
+
+	if (ret)
+		return ret;
+
+	switch (b) {
+	case BE_C_HSMIC:
+		ret = set_route_by_array(mixer,
+				g_capture_be_headsetmic_mix, enable);
+		break;
+	case BE_C_OBMIC:
+		ret = set_route_by_array(mixer,
+				g_capture_be_onboardmic_mix, enable);
+		break;
+	case BE_C_AUXFM:
+		ret = set_route_by_array(mixer,
+				g_capture_be_auxfm_mix, enable);
+		break;
+	case BE_C_DMIC0:
+		ret = set_route_by_array(mixer,
+				g_capture_be_dmic0_mix, enable);
+		break;
+	case BE_C_DMIC1:
+		ret = set_route_by_array(mixer,
+				g_capture_be_dmic1_mix, enable);
+		break;
+	case BE_C_DMIC2:
+		ret = set_route_by_array(mixer,
+				g_capture_be_dmic2_mix, enable);
+		break;
+	case BE_C_BT:
+		ret = set_route_by_array(mixer,
+				g_capture_be_bluetooth_mix, enable);
+		break;
+	case BE_C_ECHO:
+		ret = set_route_by_array(mixer,
+				g_capture_be_echo_mix, enable);
+		break;
+	case BE_C_VXREC:
+		ret = set_route_by_array(mixer,
+				g_capture_be_vxrec_mix, enable);
+		break;
+	default:
+		assert(0);
+	}
+
+	return ret;
+}
+
+static int config(struct mixer *mixer, int direction, const char* fe,
+	const char* be, int enable, int *optional_port)
+{
+	if (g_abe_api == ABE_API_NULL) {
+		struct audio_tool_mixer_cache cache;
+		mixer_cache_init(&cache);
+		if(mixer_cache_populate(&cache, mixer)) {
+			fprintf(stderr, "Error: could not populate mixer cache for card\n");
+			return ENODEV;
+		}
+		if(detect_abe_api(&cache)) {
+			fprintf(stderr, "Error: could not detect ABE version\n");
+			return ENODEV;
+		}
+		mixer_cache_deinit(&cache);
+	}
+
+	if (direction == AUDIO_DIRECTION_PLAYBACK) {
+		return config_playback(mixer, fe, be, enable, optional_port);
+	} else {
+		return config_capture(mixer, fe, be, enable, optional_port);
+	}
+}
+
+/* NULL terminated list: */
+static const char* supported_cards[] = {
+	"OMAP45",
+	"SDP4430",
+	"OMAP5",
+	"OMAP5EVM",
+	"Tablet44xx",
+	"Panda",
+	NULL
+};
+
+static int probe(void)
+{
+	int card = -ENODEV;
+	const char** cardname;
+
+	for (cardname = supported_cards ; *cardname ; ++cardname) {
+		card = ah_card_find_by_name(*cardname);
+
+		if (card >= 0)
+			return 0;
+	}
+
+	return ENODEV;
+
+}
+
+static struct audio_tool_card_module g_omap_abe_mod_template = {
+	.type = AUDIO_TOOL_MOD_TYPE_CARD,
+	.name = "",
+	.probe = probe,
+	.get_mixer_defaults = get_mixer_defaults,
+	.get_fe_be_names = get_fe_be_names,
+	.config = config,
+};
+
+static void __init init(void)
+{
+	const char** cardname;
+	int card;
+	int ret;
+
+	for (cardname = supported_cards ; *cardname ; ++cardname) {
+		card = ah_card_find_by_name(*cardname);
+
+		if (card < 0)
+			continue;
+
+		struct audio_tool_card_module *mod;
+		mod = malloc(sizeof(struct audio_tool_card_module));
+		if (!mod) {
+			fprintf(stderr, "Error: could not allocate memory for module %s\n",
+				*cardname);
+		}
+		*mod = g_omap_abe_mod_template;
+		mod->name = *cardname;
+
+		ret = audio_tool_module_register((struct audio_tool_module*)mod);
+		if (ret) {
+			fprintf(stderr, "Error: could not register module %s (%s)\n",
+				*cardname, strerror(ret));
+		}
+	}
+
+}
diff --git a/audio-tool/cmdline.c b/audio-tool/cmdline.c
new file mode 100644
index 0000000..5bb6aeb
--- /dev/null
+++ b/audio-tool/cmdline.c
@@ -0,0 +1,708 @@
+/*
+  File autogenerated by gengetopt version 2.22.6
+  generated with the following command:
+  gengetopt --input=/src/external/audio-tool/cmdline.ggo --file-name=cmdline --unamed-opts 
+
+  The developers of gengetopt consider the fixed text that goes in all
+  gengetopt output files to be in the public domain:
+  we make no copyright claims on it.
+*/
+
+/* If we use autoconf.  */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef FIX_UNUSED
+#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */
+#endif
+
+#include <getopt.h>
+
+#include "cmdline.h"
+
+const char *gengetopt_args_info_purpose = "";
+
+const char *gengetopt_args_info_usage = "Usage: audio-tool [options] command [command args]";
+
+const char *gengetopt_args_info_versiontext = "";
+
+const char *gengetopt_args_info_description = "Audio utility knife for playback, capture, and tests\n\nCommands:\n	play <filename> - playback of an RIFF WAV file\n	cap <filename> - capture to an RIFF WAV file\n	mix <ctrl#> <value> - manipulate the ALSA mixer\n	tone <wave> <freq> <vol dB> - generate a tone (sine wave, square wave, etc)\n	pulse - generate impulses on the period boundaries\n	save - save current mixer state to a file\n	restore - restore mixer state from a file\n	defaults - put card in audio-tool's 'default' state\n";
+
+const char *gengetopt_args_info_help[] = {
+  "  -h, --help              Print help and exit",
+  "  -V, --version           Print version and exit",
+  "\nOptions:",
+  "  -D, --card=INT          audio card number  (default=`0')",
+  "  -d, --device=INT        audio port  (default=`0')",
+  "  -p, --periods=INT       frames per period  (default=`1032')",
+  "  -n, --num-periods=INT   number of periods per buffer  (default=`4')",
+  "  -t, --time=INT          maximum duration of command  (default=`0')",
+  "  -c, --channels=INT      number of channels  (default=`2')",
+  "  -m, --channel-mask=INT  Bit mask with enabled channels\n                            (default=`0xFFFFFFFF')",
+  "  -b, --bits=INT          Number of bits in output format (signed)\n                            (default=`16')",
+  "  -r, --rate=INT          Sample rate  (default=`48000')",
+  "\nCopyright 2012 Texas Instruments Incorporated\nCopyright 2011 The Android Open Source Project\nCopyright 2011 Gabriel M. Beddingfield\n",
+    0
+};
+
+typedef enum {ARG_NO
+  , ARG_INT
+} cmdline_parser_arg_type;
+
+static
+void clear_given (struct gengetopt_args_info *args_info);
+static
+void clear_args (struct gengetopt_args_info *args_info);
+
+static int
+cmdline_parser_internal (int argc, char **argv, struct gengetopt_args_info *args_info,
+                        struct cmdline_parser_params *params, const char *additional_error);
+
+
+static char *
+gengetopt_strdup (const char *s);
+
+static
+void clear_given (struct gengetopt_args_info *args_info)
+{
+  args_info->help_given = 0 ;
+  args_info->version_given = 0 ;
+  args_info->card_given = 0 ;
+  args_info->device_given = 0 ;
+  args_info->periods_given = 0 ;
+  args_info->num_periods_given = 0 ;
+  args_info->time_given = 0 ;
+  args_info->channels_given = 0 ;
+  args_info->channel_mask_given = 0 ;
+  args_info->bits_given = 0 ;
+  args_info->rate_given = 0 ;
+}
+
+static
+void clear_args (struct gengetopt_args_info *args_info)
+{
+  FIX_UNUSED (args_info);
+  args_info->card_arg = 0;
+  args_info->card_orig = NULL;
+  args_info->device_arg = 0;
+  args_info->device_orig = NULL;
+  args_info->periods_arg = 1032;
+  args_info->periods_orig = NULL;
+  args_info->num_periods_arg = 4;
+  args_info->num_periods_orig = NULL;
+  args_info->time_arg = 0;
+  args_info->time_orig = NULL;
+  args_info->channels_arg = 2;
+  args_info->channels_orig = NULL;
+  args_info->channel_mask_arg = 0xFFFFFFFF;
+  args_info->channel_mask_orig = NULL;
+  args_info->bits_arg = 16;
+  args_info->bits_orig = NULL;
+  args_info->rate_arg = 48000;
+  args_info->rate_orig = NULL;
+  
+}
+
+static
+void init_args_info(struct gengetopt_args_info *args_info)
+{
+
+
+  args_info->help_help = gengetopt_args_info_help[0] ;
+  args_info->version_help = gengetopt_args_info_help[1] ;
+  args_info->card_help = gengetopt_args_info_help[3] ;
+  args_info->device_help = gengetopt_args_info_help[4] ;
+  args_info->periods_help = gengetopt_args_info_help[5] ;
+  args_info->num_periods_help = gengetopt_args_info_help[6] ;
+  args_info->time_help = gengetopt_args_info_help[7] ;
+  args_info->channels_help = gengetopt_args_info_help[8] ;
+  args_info->channel_mask_help = gengetopt_args_info_help[9] ;
+  args_info->bits_help = gengetopt_args_info_help[10] ;
+  args_info->rate_help = gengetopt_args_info_help[11] ;
+  
+}
+
+void
+cmdline_parser_print_version (void)
+{
+  printf ("%s %s\n",
+     (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE),
+     CMDLINE_PARSER_VERSION);
+
+  if (strlen(gengetopt_args_info_versiontext) > 0)
+    printf("\n%s\n", gengetopt_args_info_versiontext);
+}
+
+static void print_help_common(void) {
+  cmdline_parser_print_version ();
+
+  if (strlen(gengetopt_args_info_purpose) > 0)
+    printf("\n%s\n", gengetopt_args_info_purpose);
+
+  if (strlen(gengetopt_args_info_usage) > 0)
+    printf("\n%s\n", gengetopt_args_info_usage);
+
+  printf("\n");
+
+  if (strlen(gengetopt_args_info_description) > 0)
+    printf("%s\n\n", gengetopt_args_info_description);
+}
+
+void
+cmdline_parser_print_help (void)
+{
+  int i = 0;
+  print_help_common();
+  while (gengetopt_args_info_help[i])
+    printf("%s\n", gengetopt_args_info_help[i++]);
+}
+
+void
+cmdline_parser_init (struct gengetopt_args_info *args_info)
+{
+  clear_given (args_info);
+  clear_args (args_info);
+  init_args_info (args_info);
+
+  args_info->inputs = 0;
+  args_info->inputs_num = 0;
+}
+
+void
+cmdline_parser_params_init(struct cmdline_parser_params *params)
+{
+  if (params)
+    { 
+      params->override = 0;
+      params->initialize = 1;
+      params->check_required = 1;
+      params->check_ambiguity = 0;
+      params->print_errors = 1;
+    }
+}
+
+struct cmdline_parser_params *
+cmdline_parser_params_create(void)
+{
+  struct cmdline_parser_params *params = 
+    (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params));
+  cmdline_parser_params_init(params);  
+  return params;
+}
+
+static void
+free_string_field (char **s)
+{
+  if (*s)
+    {
+      free (*s);
+      *s = 0;
+    }
+}
+
+
+static void
+cmdline_parser_release (struct gengetopt_args_info *args_info)
+{
+  unsigned int i;
+  free_string_field (&(args_info->card_orig));
+  free_string_field (&(args_info->device_orig));
+  free_string_field (&(args_info->periods_orig));
+  free_string_field (&(args_info->num_periods_orig));
+  free_string_field (&(args_info->time_orig));
+  free_string_field (&(args_info->channels_orig));
+  free_string_field (&(args_info->channel_mask_orig));
+  free_string_field (&(args_info->bits_orig));
+  free_string_field (&(args_info->rate_orig));
+  
+  
+  for (i = 0; i < args_info->inputs_num; ++i)
+    free (args_info->inputs [i]);
+
+  if (args_info->inputs_num)
+    free (args_info->inputs);
+
+  clear_given (args_info);
+}
+
+
+static void
+write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[])
+{
+  FIX_UNUSED (values);
+  if (arg) {
+    fprintf(outfile, "%s=\"%s\"\n", opt, arg);
+  } else {
+    fprintf(outfile, "%s\n", opt);
+  }
+}
+
+
+int
+cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info)
+{
+  int i = 0;
+
+  if (!outfile)
+    {
+      fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE);
+      return EXIT_FAILURE;
+    }
+
+  if (args_info->help_given)
+    write_into_file(outfile, "help", 0, 0 );
+  if (args_info->version_given)
+    write_into_file(outfile, "version", 0, 0 );
+  if (args_info->card_given)
+    write_into_file(outfile, "card", args_info->card_orig, 0);
+  if (args_info->device_given)
+    write_into_file(outfile, "device", args_info->device_orig, 0);
+  if (args_info->periods_given)
+    write_into_file(outfile, "periods", args_info->periods_orig, 0);
+  if (args_info->num_periods_given)
+    write_into_file(outfile, "num-periods", args_info->num_periods_orig, 0);
+  if (args_info->time_given)
+    write_into_file(outfile, "time", args_info->time_orig, 0);
+  if (args_info->channels_given)
+    write_into_file(outfile, "channels", args_info->channels_orig, 0);
+  if (args_info->channel_mask_given)
+    write_into_file(outfile, "channel-mask", args_info->channel_mask_orig, 0);
+  if (args_info->bits_given)
+    write_into_file(outfile, "bits", args_info->bits_orig, 0);
+  if (args_info->rate_given)
+    write_into_file(outfile, "rate", args_info->rate_orig, 0);
+  
+
+  i = EXIT_SUCCESS;
+  return i;
+}
+
+int
+cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info)
+{
+  FILE *outfile;
+  int i = 0;
+
+  outfile = fopen(filename, "w");
+
+  if (!outfile)
+    {
+      fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename);
+      return EXIT_FAILURE;
+    }
+
+  i = cmdline_parser_dump(outfile, args_info);
+  fclose (outfile);
+
+  return i;
+}
+
+void
+cmdline_parser_free (struct gengetopt_args_info *args_info)
+{
+  cmdline_parser_release (args_info);
+}
+
+/** @brief replacement of strdup, which is not standard */
+char *
+gengetopt_strdup (const char *s)
+{
+  char *result = 0;
+  if (!s)
+    return result;
+
+  result = (char*)malloc(strlen(s) + 1);
+  if (result == (char*)0)
+    return (char*)0;
+  strcpy(result, s);
+  return result;
+}
+
+int
+cmdline_parser (int argc, char **argv, struct gengetopt_args_info *args_info)
+{
+  return cmdline_parser2 (argc, argv, args_info, 0, 1, 1);
+}
+
+int
+cmdline_parser_ext (int argc, char **argv, struct gengetopt_args_info *args_info,
+                   struct cmdline_parser_params *params)
+{
+  int result;
+  result = cmdline_parser_internal (argc, argv, args_info, params, 0);
+
+  if (result == EXIT_FAILURE)
+    {
+      cmdline_parser_free (args_info);
+      exit (EXIT_FAILURE);
+    }
+  
+  return result;
+}
+
+int
+cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required)
+{
+  int result;
+  struct cmdline_parser_params params;
+  
+  params.override = override;
+  params.initialize = initialize;
+  params.check_required = check_required;
+  params.check_ambiguity = 0;
+  params.print_errors = 1;
+
+  result = cmdline_parser_internal (argc, argv, args_info, &params, 0);
+
+  if (result == EXIT_FAILURE)
+    {
+      cmdline_parser_free (args_info);
+      exit (EXIT_FAILURE);
+    }
+  
+  return result;
+}
+
+int
+cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name)
+{
+  FIX_UNUSED (args_info);
+  FIX_UNUSED (prog_name);
+  return EXIT_SUCCESS;
+}
+
+
+static char *package_name = 0;
+
+/**
+ * @brief updates an option
+ * @param field the generic pointer to the field to update
+ * @param orig_field the pointer to the orig field
+ * @param field_given the pointer to the number of occurrence of this option
+ * @param prev_given the pointer to the number of occurrence already seen
+ * @param value the argument for this option (if null no arg was specified)
+ * @param possible_values the possible values for this option (if specified)
+ * @param default_value the default value (in case the option only accepts fixed values)
+ * @param arg_type the type of this option
+ * @param check_ambiguity @see cmdline_parser_params.check_ambiguity
+ * @param override @see cmdline_parser_params.override
+ * @param no_free whether to free a possible previous value
+ * @param multiple_option whether this is a multiple option
+ * @param long_opt the corresponding long option
+ * @param short_opt the corresponding short option (or '-' if none)
+ * @param additional_error possible further error specification
+ */
+static
+int update_arg(void *field, char **orig_field,
+               unsigned int *field_given, unsigned int *prev_given, 
+               char *value, const char *possible_values[],
+               const char *default_value,
+               cmdline_parser_arg_type arg_type,
+               int check_ambiguity, int override,
+               int no_free, int multiple_option,
+               const char *long_opt, char short_opt,
+               const char *additional_error)
+{
+  char *stop_char = 0;
+  const char *val = value;
+  int found;
+  FIX_UNUSED (field);
+
+  stop_char = 0;
+  found = 0;
+
+  if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given)))
+    {
+      if (short_opt != '-')
+        fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n", 
+               package_name, long_opt, short_opt,
+               (additional_error ? additional_error : ""));
+      else
+        fprintf (stderr, "%s: `--%s' option given more than once%s\n", 
+               package_name, long_opt,
+               (additional_error ? additional_error : ""));
+      return 1; /* failure */
+    }
+
+  FIX_UNUSED (default_value);
+    
+  if (field_given && *field_given && ! override)
+    return 0;
+  if (prev_given)
+    (*prev_given)++;
+  if (field_given)
+    (*field_given)++;
+  if (possible_values)
+    val = possible_values[found];
+
+  switch(arg_type) {
+  case ARG_INT:
+    if (val) *((int *)field) = strtol (val, &stop_char, 0);
+    break;
+  default:
+    break;
+  };
+
+  /* check numeric conversion */
+  switch(arg_type) {
+  case ARG_INT:
+    if (val && !(stop_char && *stop_char == '\0')) {
+      fprintf(stderr, "%s: invalid numeric value: %s\n", package_name, val);
+      return 1; /* failure */
+    }
+    break;
+  default:
+    ;
+  };
+
+  /* store the original value */
+  switch(arg_type) {
+  case ARG_NO:
+    break;
+  default:
+    if (value && orig_field) {
+      if (no_free) {
+        *orig_field = value;
+      } else {
+        if (*orig_field)
+          free (*orig_field); /* free previous string */
+        *orig_field = gengetopt_strdup (value);
+      }
+    }
+  };
+
+  return 0; /* OK */
+}
+
+
+int
+cmdline_parser_internal (
+  int argc, char **argv, struct gengetopt_args_info *args_info,
+                        struct cmdline_parser_params *params, const char *additional_error)
+{
+  int c;	/* Character of the parsed option.  */
+
+  int error_occurred = 0;
+  struct gengetopt_args_info local_args_info;
+  
+  int override;
+  int initialize;
+  int check_required;
+  int check_ambiguity;
+  
+  package_name = argv[0];
+  
+  override = params->override;
+  initialize = params->initialize;
+  check_required = params->check_required;
+  check_ambiguity = params->check_ambiguity;
+
+  if (initialize)
+    cmdline_parser_init (args_info);
+
+  cmdline_parser_init (&local_args_info);
+
+  optarg = 0;
+  optind = 0;
+  opterr = params->print_errors;
+  optopt = '?';
+
+  while (1)
+    {
+      int option_index = 0;
+
+      static struct option long_options[] = {
+        { "help",	0, NULL, 'h' },
+        { "version",	0, NULL, 'V' },
+        { "card",	1, NULL, 'D' },
+        { "device",	1, NULL, 'd' },
+        { "periods",	1, NULL, 'p' },
+        { "num-periods",	1, NULL, 'n' },
+        { "time",	1, NULL, 't' },
+        { "channels",	1, NULL, 'c' },
+        { "channel-mask",	1, NULL, 'm' },
+        { "bits",	1, NULL, 'b' },
+        { "rate",	1, NULL, 'r' },
+        { 0,  0, 0, 0 }
+      };
+
+      c = getopt_long (argc, argv, "hVD:d:p:n:t:c:m:b:r:", long_options, &option_index);
+
+      if (c == -1) break;	/* Exit from `while (1)' loop.  */
+
+      switch (c)
+        {
+        case 'h':	/* Print help and exit.  */
+          cmdline_parser_print_help ();
+          cmdline_parser_free (&local_args_info);
+          exit (EXIT_SUCCESS);
+
+        case 'V':	/* Print version and exit.  */
+          cmdline_parser_print_version ();
+          cmdline_parser_free (&local_args_info);
+          exit (EXIT_SUCCESS);
+
+        case 'D':	/* audio card number.  */
+        
+        
+          if (update_arg( (void *)&(args_info->card_arg), 
+               &(args_info->card_orig), &(args_info->card_given),
+              &(local_args_info.card_given), optarg, 0, "0", ARG_INT,
+              check_ambiguity, override, 0, 0,
+              "card", 'D',
+              additional_error))
+            goto failure;
+        
+          break;
+        case 'd':	/* audio port.  */
+        
+        
+          if (update_arg( (void *)&(args_info->device_arg), 
+               &(args_info->device_orig), &(args_info->device_given),
+              &(local_args_info.device_given), optarg, 0, "0", ARG_INT,
+              check_ambiguity, override, 0, 0,
+              "device", 'd',
+              additional_error))
+            goto failure;
+        
+          break;
+        case 'p':	/* frames per period.  */
+        
+        
+          if (update_arg( (void *)&(args_info->periods_arg), 
+               &(args_info->periods_orig), &(args_info->periods_given),
+              &(local_args_info.periods_given), optarg, 0, "1032", ARG_INT,
+              check_ambiguity, override, 0, 0,
+              "periods", 'p',
+              additional_error))
+            goto failure;
+        
+          break;
+        case 'n':	/* number of periods per buffer.  */
+        
+        
+          if (update_arg( (void *)&(args_info->num_periods_arg), 
+               &(args_info->num_periods_orig), &(args_info->num_periods_given),
+              &(local_args_info.num_periods_given), optarg, 0, "4", ARG_INT,
+              check_ambiguity, override, 0, 0,
+              "num-periods", 'n',
+              additional_error))
+            goto failure;
+        
+          break;
+        case 't':	/* maximum duration of command.  */
+        
+        
+          if (update_arg( (void *)&(args_info->time_arg), 
+               &(args_info->time_orig), &(args_info->time_given),
+              &(local_args_info.time_given), optarg, 0, "0", ARG_INT,
+              check_ambiguity, override, 0, 0,
+              "time", 't',
+              additional_error))
+            goto failure;
+        
+          break;
+        case 'c':	/* number of channels.  */
+        
+        
+          if (update_arg( (void *)&(args_info->channels_arg), 
+               &(args_info->channels_orig), &(args_info->channels_given),
+              &(local_args_info.channels_given), optarg, 0, "2", ARG_INT,
+              check_ambiguity, override, 0, 0,
+              "channels", 'c',
+              additional_error))
+            goto failure;
+        
+          break;
+        case 'm':	/* Bit mask with enabled channels.  */
+        
+        
+          if (update_arg( (void *)&(args_info->channel_mask_arg), 
+               &(args_info->channel_mask_orig), &(args_info->channel_mask_given),
+              &(local_args_info.channel_mask_given), optarg, 0, "0xFFFFFFFF", ARG_INT,
+              check_ambiguity, override, 0, 0,
+              "channel-mask", 'm',
+              additional_error))
+            goto failure;
+        
+          break;
+        case 'b':	/* Number of bits in output format (signed).  */
+        
+        
+          if (update_arg( (void *)&(args_info->bits_arg), 
+               &(args_info->bits_orig), &(args_info->bits_given),
+              &(local_args_info.bits_given), optarg, 0, "16", ARG_INT,
+              check_ambiguity, override, 0, 0,
+              "bits", 'b',
+              additional_error))
+            goto failure;
+        
+          break;
+        case 'r':	/* Sample rate.  */
+        
+        
+          if (update_arg( (void *)&(args_info->rate_arg), 
+               &(args_info->rate_orig), &(args_info->rate_given),
+              &(local_args_info.rate_given), optarg, 0, "48000", ARG_INT,
+              check_ambiguity, override, 0, 0,
+              "rate", 'r',
+              additional_error))
+            goto failure;
+        
+          break;
+
+        case 0:	/* Long option with no short option */
+        case '?':	/* Invalid option.  */
+          /* `getopt_long' already printed an error message.  */
+          goto failure;
+
+        default:	/* bug: option not considered.  */
+          fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : ""));
+          abort ();
+        } /* switch */
+    } /* while */
+
+
+
+
+  cmdline_parser_release (&local_args_info);
+
+  if ( error_occurred )
+    return (EXIT_FAILURE);
+
+  if (optind < argc)
+    {
+      int i = 0 ;
+      int found_prog_name = 0;
+      /* whether program name, i.e., argv[0], is in the remaining args
+         (this may happen with some implementations of getopt,
+          but surely not with the one included by gengetopt) */
+
+      i = optind;
+      while (i < argc)
+        if (argv[i++] == argv[0]) {
+          found_prog_name = 1;
+          break;
+        }
+      i = 0;
+
+      args_info->inputs_num = argc - optind - found_prog_name;
+      args_info->inputs =
+        (char **)(malloc ((args_info->inputs_num)*sizeof(char *))) ;
+      while (optind < argc)
+        if (argv[optind++] != argv[0])
+          args_info->inputs[ i++ ] = gengetopt_strdup (argv[optind-1]) ;
+    }
+
+  return 0;
+
+failure:
+  
+  cmdline_parser_release (&local_args_info);
+  return (EXIT_FAILURE);
+}
diff --git a/audio-tool/cmdline.ggo b/audio-tool/cmdline.ggo
new file mode 100644
index 0000000..fb0f282
--- /dev/null
+++ b/audio-tool/cmdline.ggo
@@ -0,0 +1,78 @@
+package "audio-tool"
+usage "audio-tool [options] command [command args]"
+description "Audio utility knife for playback, capture, and tests
+
+Commands:
+	play <filename> - playback of an RIFF WAV file
+	cap <filename> - capture to an RIFF WAV file
+	mix <ctrl#> <value> - manipulate the ALSA mixer
+	tone <wave> <freq> <vol dB> - generate a tone (sine wave, square wave, etc)
+	pulse - generate impulses on the period boundaries
+	save - save current mixer state to a file
+	restore - restore mixer state from a file
+	defaults - put card in audio-tool's 'default' state
+"
+
+section "Options"
+
+option "card" D
+       "audio card number"
+       int
+       default="0"
+       optional
+
+option "device" d
+       "audio port"
+       int
+       default="0"
+       optional
+
+option "periods" p
+       "frames per period"
+       int
+       default="1032"
+       optional
+
+option "num-periods" n
+       "number of periods per buffer"
+       int
+       default="4"
+       optional
+
+option "time" t
+       "maximum duration of command"
+       int
+       default="0"
+       optional
+
+option "channels" c
+       "number of channels"
+       int
+       default="2"
+       optional
+
+option "channel-mask" m
+       "Bit mask with enabled channels"
+       int
+       default="0xFFFFFFFF"
+       optional
+
+option "bits" b
+       "Number of bits in output format (signed)"
+       int
+       default="16"
+       optional
+
+option "rate" r
+       "Sample rate"
+       int
+       default="48000"
+       optional
+
+section "Copyrights"
+
+text "
+Copyright 2012 Texas Instruments Incorporated
+Copyright 2011 The Android Open Source Project
+Copyright 2011 Gabriel M. Beddingfield
+"
\ No newline at end of file
diff --git a/audio-tool/cmdline.h b/audio-tool/cmdline.h
new file mode 100644
index 0000000..4bfd7d6
--- /dev/null
+++ b/audio-tool/cmdline.h
@@ -0,0 +1,211 @@
+/** @file cmdline.h
+ *  @brief The header file for the command line option parser
+ *  generated by GNU Gengetopt version 2.22.6
+ *  http://www.gnu.org/software/gengetopt.
+ *  DO NOT modify this file, since it can be overwritten
+ *  @author GNU Gengetopt by Lorenzo Bettini */
+
+#ifndef CMDLINE_H
+#define CMDLINE_H
+
+/* If we use autoconf.  */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h> /* for FILE */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef CMDLINE_PARSER_PACKAGE
+/** @brief the program name (used for printing errors) */
+#define CMDLINE_PARSER_PACKAGE "audio-tool"
+#endif
+
+#ifndef CMDLINE_PARSER_PACKAGE_NAME
+/** @brief the complete program name (used for help and version) */
+#define CMDLINE_PARSER_PACKAGE_NAME "audio-tool"
+#endif
+
+#ifndef CMDLINE_PARSER_VERSION
+/** @brief the program version */
+#define CMDLINE_PARSER_VERSION VERSION
+#endif
+
+/** @brief Where the command line options are stored */
+struct gengetopt_args_info
+{
+  const char *help_help; /**< @brief Print help and exit help description.  */
+  const char *version_help; /**< @brief Print version and exit help description.  */
+  int card_arg;	/**< @brief audio card number (default='0').  */
+  char * card_orig;	/**< @brief audio card number original value given at command line.  */
+  const char *card_help; /**< @brief audio card number help description.  */
+  int device_arg;	/**< @brief audio port (default='0').  */
+  char * device_orig;	/**< @brief audio port original value given at command line.  */
+  const char *device_help; /**< @brief audio port help description.  */
+  int periods_arg;	/**< @brief frames per period (default='1032').  */
+  char * periods_orig;	/**< @brief frames per period original value given at command line.  */
+  const char *periods_help; /**< @brief frames per period help description.  */
+  int num_periods_arg;	/**< @brief number of periods per buffer (default='4').  */
+  char * num_periods_orig;	/**< @brief number of periods per buffer original value given at command line.  */
+  const char *num_periods_help; /**< @brief number of periods per buffer help description.  */
+  int time_arg;	/**< @brief maximum duration of command (default='0').  */
+  char * time_orig;	/**< @brief maximum duration of command original value given at command line.  */
+  const char *time_help; /**< @brief maximum duration of command help description.  */
+  int channels_arg;	/**< @brief number of channels (default='2').  */
+  char * channels_orig;	/**< @brief number of channels original value given at command line.  */
+  const char *channels_help; /**< @brief number of channels help description.  */
+  int channel_mask_arg;	/**< @brief Bit mask with enabled channels (default='0xFFFFFFFF').  */
+  char * channel_mask_orig;	/**< @brief Bit mask with enabled channels original value given at command line.  */
+  const char *channel_mask_help; /**< @brief Bit mask with enabled channels help description.  */
+  int bits_arg;	/**< @brief Number of bits in output format (signed) (default='16').  */
+  char * bits_orig;	/**< @brief Number of bits in output format (signed) original value given at command line.  */
+  const char *bits_help; /**< @brief Number of bits in output format (signed) help description.  */
+  int rate_arg;	/**< @brief Sample rate (default='48000').  */
+  char * rate_orig;	/**< @brief Sample rate original value given at command line.  */
+  const char *rate_help; /**< @brief Sample rate help description.  */
+  
+  unsigned int help_given ;	/**< @brief Whether help was given.  */
+  unsigned int version_given ;	/**< @brief Whether version was given.  */
+  unsigned int card_given ;	/**< @brief Whether card was given.  */
+  unsigned int device_given ;	/**< @brief Whether device was given.  */
+  unsigned int periods_given ;	/**< @brief Whether periods was given.  */
+  unsigned int num_periods_given ;	/**< @brief Whether num-periods was given.  */
+  unsigned int time_given ;	/**< @brief Whether time was given.  */
+  unsigned int channels_given ;	/**< @brief Whether channels was given.  */
+  unsigned int channel_mask_given ;	/**< @brief Whether channel-mask was given.  */
+  unsigned int bits_given ;	/**< @brief Whether bits was given.  */
+  unsigned int rate_given ;	/**< @brief Whether rate was given.  */
+
+  char **inputs ; /**< @brief unamed options (options without names) */
+  unsigned inputs_num ; /**< @brief unamed options number */
+} ;
+
+/** @brief The additional parameters to pass to parser functions */
+struct cmdline_parser_params
+{
+  int override; /**< @brief whether to override possibly already present options (default 0) */
+  int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */
+  int check_required; /**< @brief whether to check that all required options were provided (default 1) */
+  int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */
+  int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */
+} ;
+
+/** @brief the purpose string of the program */
+extern const char *gengetopt_args_info_purpose;
+/** @brief the usage string of the program */
+extern const char *gengetopt_args_info_usage;
+/** @brief the description string of the program */
+extern const char *gengetopt_args_info_description;
+/** @brief all the lines making the help output */
+extern const char *gengetopt_args_info_help[];
+
+/**
+ * The command line parser
+ * @param argc the number of command line options
+ * @param argv the command line options
+ * @param args_info the structure where option information will be stored
+ * @return 0 if everything went fine, NON 0 if an error took place
+ */
+int cmdline_parser (int argc, char **argv,
+  struct gengetopt_args_info *args_info);
+
+/**
+ * The command line parser (version with additional parameters - deprecated)
+ * @param argc the number of command line options
+ * @param argv the command line options
+ * @param args_info the structure where option information will be stored
+ * @param override whether to override possibly already present options
+ * @param initialize whether to initialize the option structure my_args_info
+ * @param check_required whether to check that all required options were provided
+ * @return 0 if everything went fine, NON 0 if an error took place
+ * @deprecated use cmdline_parser_ext() instead
+ */
+int cmdline_parser2 (int argc, char **argv,
+  struct gengetopt_args_info *args_info,
+  int override, int initialize, int check_required);
+
+/**
+ * The command line parser (version with additional parameters)
+ * @param argc the number of command line options
+ * @param argv the command line options
+ * @param args_info the structure where option information will be stored
+ * @param params additional parameters for the parser
+ * @return 0 if everything went fine, NON 0 if an error took place
+ */
+int cmdline_parser_ext (int argc, char **argv,
+  struct gengetopt_args_info *args_info,
+  struct cmdline_parser_params *params);
+
+/**
+ * Save the contents of the option struct into an already open FILE stream.
+ * @param outfile the stream where to dump options
+ * @param args_info the option struct to dump
+ * @return 0 if everything went fine, NON 0 if an error took place
+ */
+int cmdline_parser_dump(FILE *outfile,
+  struct gengetopt_args_info *args_info);
+
+/**
+ * Save the contents of the option struct into a (text) file.
+ * This file can be read by the config file parser (if generated by gengetopt)
+ * @param filename the file where to save
+ * @param args_info the option struct to save
+ * @return 0 if everything went fine, NON 0 if an error took place
+ */
+int cmdline_parser_file_save(const char *filename,
+  struct gengetopt_args_info *args_info);
+
+/**
+ * Print the help
+ */
+void cmdline_parser_print_help(void);
+/**
+ * Print the version
+ */
+void cmdline_parser_print_version(void);
+
+/**
+ * Initializes all the fields a cmdline_parser_params structure 
+ * to their default values
+ * @param params the structure to initialize
+ */
+void cmdline_parser_params_init(struct cmdline_parser_params *params);
+
+/**
+ * Allocates dynamically a cmdline_parser_params structure and initializes
+ * all its fields to their default values
+ * @return the created and initialized cmdline_parser_params structure
+ */
+struct cmdline_parser_params *cmdline_parser_params_create(void);
+
+/**
+ * Initializes the passed gengetopt_args_info structure's fields
+ * (also set default values for options that have a default)
+ * @param args_info the structure to initialize
+ */
+void cmdline_parser_init (struct gengetopt_args_info *args_info);
+/**
+ * Deallocates the string fields of the gengetopt_args_info structure
+ * (but does not deallocate the structure itself)
+ * @param args_info the structure to deallocate
+ */
+void cmdline_parser_free (struct gengetopt_args_info *args_info);
+
+/**
+ * Checks that all the required options were specified
+ * @param args_info the structure to check
+ * @param prog_name the name of the program that will be used to print
+ *   possible errors
+ * @return
+ */
+int cmdline_parser_required (struct gengetopt_args_info *args_info,
+  const char *prog_name);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* CMDLINE_H */
diff --git a/audio-tool/config.c b/audio-tool/config.c
new file mode 100644
index 0000000..b398eb1
--- /dev/null
+++ b/audio-tool/config.c
@@ -0,0 +1,73 @@
+/*
+ * config.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "config.h"
+#include "cmdline.h"
+
+#include <assert.h>
+
+int parse_args(struct audio_tool_config *conf, int *argc, char*** argv)
+{
+	int ret;
+	struct gengetopt_args_info args_info;
+
+	assert(conf);
+
+	ret = cmdline_parser(*argc, *argv, &args_info);
+
+	if (!ret) {
+		conf->card = args_info.card_arg;
+		conf->device = args_info.device_arg;
+		conf->period_size = args_info.periods_arg;
+		conf->num_periods = args_info.num_periods_arg;
+		conf->duration = args_info.time_arg;
+		conf->channels = args_info.channels_arg;
+		conf->channel_mask = (uint32_t) args_info.channel_mask_arg;
+		conf->bits = args_info.bits_arg;
+		conf->rate = args_info.rate_arg;
+
+		*argc = args_info.inputs_num;
+		*argv = args_info.inputs;
+	}
+
+	return ret;
+}
+
+void usage(void)
+{
+	cmdline_parser_print_help();
+}
+
diff --git a/audio-tool/config.h b/audio-tool/config.h
new file mode 100644
index 0000000..c83e305
--- /dev/null
+++ b/audio-tool/config.h
@@ -0,0 +1,55 @@
+/*
+ * config.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __AUDIO_TOOL_CONFIG_H__
+#define __AUDIO_TOOL_CONFIG_H__
+
+#include <stdint.h>
+
+struct audio_tool_config {
+	int card;
+	int device;
+	int period_size;
+	int num_periods;
+	int duration;
+	int channels;
+	uint32_t channel_mask;
+	int bits;
+	int rate;
+};
+
+#endif /* __OMAP_AUDIO_TOOL_CONFIG_H__ */
+
diff --git a/audio-tool/config_cmd.c b/audio-tool/config_cmd.c
new file mode 100644
index 0000000..f4c983c
--- /dev/null
+++ b/audio-tool/config_cmd.c
@@ -0,0 +1,228 @@
+/*
+ * config_cmd.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <tinyalsa/asoundlib.h>
+
+#include "config.h"
+#include "config_cmd.h"
+#include "module.h"
+#include "alsa-control.h"
+
+#define BUFSIZE 512
+
+static void usage(int card)
+{
+	char **p_fes, **p_bes, **c_fes, **c_bes;
+	char cardname[BUFSIZE];
+	struct audio_tool_card_module *mod;
+	int ret;
+
+	ret = ah_card_get_name(card, cardname, BUFSIZE);
+	if (ret) {
+		fprintf(stderr, "Error: couldn't get name for card %d (%s)\n",
+			card, strerror(-ret));
+		return;
+	}
+
+	mod = (struct audio_tool_card_module*)audio_tool_get_module(
+		AUDIO_TOOL_MOD_TYPE_CARD, cardname);
+	if (!mod) {
+		fprintf(stderr, "Error: couldn't get module for card %d\n",
+			card);
+		return;
+	}
+
+	ret = mod->get_fe_be_names(AUDIO_DIRECTION_PLAYBACK, &p_fes, &p_bes);
+	if (ret) {
+		fprintf(stderr, "Error: couldn't get listing of FE's/BE's for card\n");
+		return;
+	}
+	ret = mod->get_fe_be_names(AUDIO_DIRECTION_CAPTURE, &c_fes, &c_bes);
+	if (ret) {
+		fprintf(stderr, "Error: couldn't get listing of FE's/BE's for card\n");
+		return;
+	}
+
+	printf("Usage: audio-tool config <play|cap> <frontend> <backend> <1|0|disable|enable>\n");
+	printf("\n");
+	printf("For card %d, the options are:\n", card);
+	printf("PLAYBACK (play):\n");
+	printf("  Frontends:");
+	while (*p_fes) {
+		printf(" %s", *p_fes);
+		++p_fes;
+	}
+	printf("\n");
+	printf("  Backends:");
+	while (*p_bes) {
+		printf(" %s", *p_bes);
+		++p_bes;
+	}
+	printf("\n");
+	printf("CAPTURE (cap):\n");
+	mod->get_fe_be_names(AUDIO_DIRECTION_CAPTURE, &c_fes, &c_bes);
+	printf("  Frontends:");
+	while (*c_fes) {
+		printf(" %s", *c_fes);
+		++c_fes;
+	}
+	printf("\n");
+	printf("  Backends:");
+	while (*c_bes) {
+		printf(" %s", *c_bes);
+		++c_bes;
+	}
+	printf("\n");
+	printf("\n");
+	printf("To see options for a different card, use the -D option\n");
+}
+
+int config_cmd_main(const struct audio_tool_config *config, int argc, char **argv)
+{
+	struct mixer *mixer;
+	struct audio_tool_card_module *mod;
+	char *dir_arg, *fe_arg, *be_arg, *enable_arg;
+	char **fes, **bes;
+	char cardname[BUFSIZE];
+	int card = config->card;
+	int ret = 0;
+	int direction;
+	int port = 0;
+	int enable;
+
+	if (argc < 5) {
+		usage(card);
+		return 0;
+	}
+
+	dir_arg = argv[1];
+	fe_arg = argv[2];
+	be_arg = argv[3];
+	enable_arg = argv[4];
+
+	if (0 == strcmp(dir_arg, "play")) {
+		direction = AUDIO_DIRECTION_PLAYBACK;
+	} else if (0 == strcmp(dir_arg, "cap")) {
+		direction = AUDIO_DIRECTION_CAPTURE;
+	} else {
+		fprintf(stderr, "Error: %s is not 'play' or 'cap'\n", dir_arg);
+		usage(card);
+		return 1;
+	}
+
+	if ((0 == strcmp(enable_arg, "1")) || (0 == strcmp(enable_arg, "enable"))) {
+		enable = 1;
+	} else if ((0 == strcmp(enable_arg, "0")) || (0 == strcmp(enable_arg, "disable"))) {
+		enable = 0;
+	} else {
+		fprintf(stderr, "Error: '%s' is not '0', '1', 'disable', or 'enable'\n",
+			enable_arg);
+		return 1;
+	}
+
+	ret = ah_card_get_name(card, cardname, BUFSIZE);
+	if (ret) {
+		fprintf(stderr, "Error: couldn't get name for card %d (%s)\n",
+			card, strerror(-ret));
+		return ret;
+	}
+
+	mod = (struct audio_tool_card_module*)audio_tool_get_module(
+		AUDIO_TOOL_MOD_TYPE_CARD, cardname);
+	if (!mod) {
+		fprintf(stderr, "Error: couldn't get module for card %d\n",
+			card);
+		return ret;
+	}
+
+	ret = EINVAL;
+	if (direction == AUDIO_DIRECTION_PLAYBACK) {
+		ret = mod->get_fe_be_names(AUDIO_DIRECTION_PLAYBACK, &fes, &bes);
+	} else {
+		ret = mod->get_fe_be_names(AUDIO_DIRECTION_CAPTURE, &fes, &bes);
+	}
+	if (ret) {
+		fprintf(stderr, "Error: could not get frontends and backends for this device\n");
+		return ret;
+	}
+
+	/* check that user args are in fe/be lists */
+	while (*fes) {
+		if (0 == strcmp(*fes, fe_arg))
+			break;
+		++fes;
+	}
+	if (!*fes) {
+		fprintf(stderr, "Error: '%s' is not a supported frontend\n", fe_arg);
+		return 1;
+	}
+	while (*bes) {
+		if (0 == strcmp(*bes, be_arg))
+			break;
+		++bes;
+	}
+	if (!*bes) {
+		fprintf(stderr, "Error: '%s' is not a supported backend\n", be_arg);
+		return 1;
+	}
+
+	mixer = mixer_open(card);
+	if (!mixer) {
+		fprintf(stderr, "Error: could not open mixer for card %d\n", card);
+		return 1;
+	}
+
+	ret = mod->config(mixer, direction, fe_arg, be_arg, enable, &port);
+
+	if (ret) {
+		fprintf(stderr, "Error: could not configure path (%s)\n", strerror(-ret));
+	} else {
+		printf("Path configured. Use card %d port %d\n", card, port);
+	}
+
+	return ret;
+}
+
diff --git a/audio-tool/config_cmd.h b/audio-tool/config_cmd.h
new file mode 100644
index 0000000..e8f7526
--- /dev/null
+++ b/audio-tool/config_cmd.h
@@ -0,0 +1,43 @@
+/*
+ * config_cmd.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __AUDIO_TOOL_CONFIG_CMD_H__
+#define __AUDIO_TOOL_CONFIG_CMD_H__
+
+int config_cmd_main(const struct audio_tool_config *config, int argc, char **argv);
+
+#endif /* __OMAP_AUDIO_TOOL_CONFIG_CMD_H__ */
+
diff --git a/audio-tool/defaults.c b/audio-tool/defaults.c
new file mode 100644
index 0000000..b8d6874
--- /dev/null
+++ b/audio-tool/defaults.c
@@ -0,0 +1,123 @@
+/*
+ * defaults.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <tinyalsa/asoundlib.h>
+
+#include "defaults.h"
+#include "config.h"
+#include "alsa-control.h"
+#include "mixer_cache.h"
+#include "module.h"
+
+#define BUFSIZE 512
+
+int defaults_main(const struct audio_tool_config *config, int argc, char **argv)
+{
+	struct audio_tool_card_module *card_mod;
+	struct mixer *mixer;
+	struct audio_tool_mixer_cache cache;
+	char buf[BUFSIZE] = "";
+	int card = config->card;
+	int cards = ah_card_count();
+	int ret = 0;
+
+	if (card < 0 || card >= cards) {
+		fprintf(stderr, "Error: card %d does not exist\n", card);
+		ret = 1;
+		goto end;
+	}
+
+	ret = ah_card_get_name(card, buf, BUFSIZE);
+	if (ret) {
+		fprintf(stderr, "Error: can't get name of card %d (%s)",
+			card, strerror(-ret));
+		ret = 1;
+		goto end;
+	}
+
+	card_mod = (struct audio_tool_card_module*)audio_tool_get_module(
+		AUDIO_TOOL_MOD_TYPE_CARD, buf);
+	if (!card_mod) {
+		fprintf(stderr, "Error: could not find the defaults for this card (%s)\n",
+			buf);
+		ret = 1;
+		goto end;
+	}
+
+	mixer = mixer_open(card);
+	if (!mixer) {
+		fprintf(stderr, "Error: could not open mixer device (%s)\n",
+			strerror(errno));
+		ret = 1;
+		goto end;
+	}
+
+	ret = mixer_cache_init(&cache);
+	if (ret) {
+		fprintf(stderr, "Error: could not initialize the mixer cache (%s)\n",
+			strerror(ret));
+		ret = 1;
+		goto mixer_cache_err;
+	}
+
+	ret = mixer_cache_populate(&cache, mixer);
+	if (ret) {
+		fprintf(stderr, "Error: could not populate the mixer cache (%s)\n",
+			strerror(-ret));
+		ret = 1;
+		goto mixer_cache_pop_err;
+	}
+
+	if (card_mod->get_mixer_defaults(&cache)) {
+		fprintf(stderr, "Warning: mixer defaults mismatched\n");
+	}
+
+	if (mixer_cache_apply(&cache, mixer)) {
+		fprintf(stderr, "Error: could not apply mixer setting\n");
+		ret = 1;
+		goto mixer_cache_pop_err;
+	}
+
+mixer_cache_pop_err:
+	mixer_cache_deinit(&cache);
+mixer_cache_err:
+	mixer_close(mixer);
+end:
+
+	return ret;
+}
diff --git a/audio-tool/defaults.h b/audio-tool/defaults.h
new file mode 100644
index 0000000..61fdf1c
--- /dev/null
+++ b/audio-tool/defaults.h
@@ -0,0 +1,45 @@
+/*
+ * defaults.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __AUDIO_TOOL_DEFAULTS_H__
+#define __AUDIO_TOOL_DEFAULTS_H__
+
+struct audio_tool_config;
+
+int defaults_main(const struct audio_tool_config *config, int argc, char **argv);
+
+#endif /* __OMAP_AUDIO_TOOL_DEFAULTS_H__ */
+
diff --git a/audio-tool/generate-wave-table.c b/audio-tool/generate-wave-table.c
new file mode 100644
index 0000000..aac91a0
--- /dev/null
+++ b/audio-tool/generate-wave-table.c
@@ -0,0 +1,370 @@
+/* Copyright (c) 2011, Gabriel M. Beddingfield <gabrbedd@gmail.com>
+ * 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.
+ *     * Neither the name of the <organization> nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 <COPYRIGHT HOLDER> 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.
+ */
+
+/*
+ * generate-wave-table.c
+ *
+ * Creates a static wave-table that can be used later as a look-up table
+ * for synthesis.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <assert.h>
+
+typedef enum {
+	DATA_TYPE_NONE = 0,
+	DATA_TYPE_S16,
+	DATA_TYPE_S32,
+} data_t;
+
+typedef enum {
+	WAVE_TYPE_SINE = 1,
+	WAVE_TYPE_COSINE,
+	WAVE_TYPE_SQUARE,
+	WAVE_TYPE_TRIANGLE,
+	WAVE_TYPE_SAW,
+} wave_t;
+
+struct signal_spec {
+	data_t type;
+	wave_t wave;
+	unsigned length;
+};
+
+typedef int (*table_generator_func)(struct signal_spec *spec,
+				    unsigned offset, unsigned count, void *buf);
+typedef int (*table_output_func)(void *buf, data_t type, unsigned count); 
+
+#define FS_S16 0x7FFF
+#define FS_S32 0x7FFFFFFF
+
+int square_wave_generator(struct signal_spec *spec, unsigned offset,
+			  unsigned count, void *buf)
+{
+	int16_t s16, *s16ptr;
+	int32_t s32, *s32ptr;
+	unsigned midpoint, k, end;
+
+	assert( spec );
+
+	if ((offset + count) > spec->length) {
+		fprintf(stderr, "Warning: generating square wave beyond "
+			"the predetermined length.\n");
+	}
+	
+	midpoint = spec->length / 2;
+	if (midpoint > offset)
+		midpoint -= offset;
+	else
+		midpoint = 0;
+	end = spec->length;
+	if (end > offset)
+		end -= offset;
+	else
+		end = 0;
+
+	if (spec->type == DATA_TYPE_S16) {
+		s16ptr = (int16_t*)buf;
+		for (k = 0 ; k < count ; ++k) {
+			if (k < midpoint) {
+				s16 = FS_S16;
+			} else {
+				s16 = -FS_S16;
+			}
+			s16ptr[k] = s16;
+		}
+	} else if (spec->type == DATA_TYPE_S32) {
+		s32ptr = (int32_t*)buf;
+		for (k = 0 ; k < count ; ++k) {
+			if (k < midpoint) {
+				s32 = FS_S32;
+			} else {
+				s32 = -FS_S32;
+			}
+			s32ptr[k] = s32;
+		}
+	} else {
+		assert( 0 );
+	}
+
+	return 0;
+}
+
+int sine_wave_generator(struct signal_spec *spec, unsigned offset,
+			  unsigned count, void *buf)
+{
+	int16_t s16, *s16ptr;
+	int32_t s32, *s32ptr;
+	double value, angle;
+	unsigned k;
+
+	assert( spec );
+
+	if (spec->type == DATA_TYPE_S16) {
+		s16ptr = (int16_t*)buf;
+		for (k = 0 ; k < count ; ++k) {
+			angle = 2.0 * M_PI * ((double)(offset + k)) / ((double)spec->length);
+			value = sin(angle) * FS_S16;
+			s16 = round(value);
+			s16ptr[k] = s16;
+		}
+	} else if (spec->type == DATA_TYPE_S32) {
+		s32ptr = (int32_t*)buf;
+		for (k = 0 ; k < count ; ++k) {
+			angle = 2.0 * M_PI * ((double)(offset + k)) / ((double)spec->length);
+			value = sin(angle) * FS_S32;
+			s32 = round(value);
+			s32ptr[k] = s32;
+		}
+	} else {
+		assert( 0 );
+	}
+
+	return 0;
+}
+
+int triangle_wave_generator(struct signal_spec *spec, unsigned offset,
+			  unsigned count, void *buf)
+{
+	int16_t s16, *s16ptr;
+	int32_t s32, *s32ptr;
+	double value;
+	unsigned k;
+
+	assert( spec );
+
+	if (spec->type == DATA_TYPE_S16) {
+		s16ptr = (int16_t*)buf;
+		for (k = 0 ; k < count ; ++k) {
+			value = 4.0 * ((double)(offset + k)) / ((double)spec->length);
+			if (value <= 1.0) {
+				/* NO-OP */
+			} else if (value <= 3.0) {
+				value = 2.0 - value;
+			} else {
+				value = value - 4.0;
+			}
+			s16 = round(value * FS_S16);
+			s16ptr[k] = s16;
+		}
+	} else if (spec->type == DATA_TYPE_S32) {
+		s32ptr = (int32_t*)buf;
+		for (k = 0 ; k < count ; ++k) {
+			value = 4.0 * ((double)(offset + k)) / ((double)spec->length);
+			if (value <= 1.0) {
+				/* NO-OP */
+			} else if (value <= 3.0) {
+				value = 2.0 - value;
+			} else {
+				value = value - 4.0;
+			}
+			s32 = round(value * FS_S32);
+			s32ptr[k] = s32;
+		}
+	} else {
+		assert( 0 );
+	}
+
+	return 0;
+}
+
+int sawtooth_wave_generator(struct signal_spec *spec, unsigned offset,
+			  unsigned count, void *buf)
+{
+	int16_t s16, *s16ptr;
+	int32_t s32, *s32ptr;
+	double value;
+	unsigned k;
+
+	assert( spec );
+
+	if (spec->type == DATA_TYPE_S16) {
+		s16ptr = (int16_t*)buf;
+		for (k = 0 ; k < count ; ++k) {
+			value = 2.0 * ((double)(offset + k)) / ((double)spec->length) - 1.0;
+			s16 = round(value * FS_S16);
+			s16ptr[k] = s16;
+		}
+	} else if (spec->type == DATA_TYPE_S32) {
+		s32ptr = (int32_t*)buf;
+		for (k = 0 ; k < count ; ++k) {
+			value = 2.0 * ((double)(offset + k)) / ((double)spec->length) - 1.0;
+			s32 = round(value * FS_S32);
+			s32ptr[k] = s32;
+		}
+	} else {
+		assert( 0 );
+	}
+
+	return 0;
+}
+
+int stdout_c_header_table(void *buf, data_t type, unsigned count)
+{
+	int16_t *s16ptr = (int16_t*)buf;
+	int32_t *s32ptr = (int32_t*)buf;
+	unsigned k;
+
+	assert( buf );
+
+	if (type == DATA_TYPE_S16) {
+		for (k=0 ; k<count ; ++k) {
+			fprintf(stdout, "0x%04hx,\n", s16ptr[k]);
+		}
+	} else if (type == DATA_TYPE_S32) {
+		for (k=0 ; k<count ; ++k) {
+			fprintf(stdout, "0x%08x,\n", s32ptr[k]);
+		}
+	} else {
+		assert( 0 );
+	}
+
+	return 0;
+}
+
+struct wave_type {
+	const char* name;
+	table_generator_func generator;
+	wave_t wave_id;
+};
+
+static struct wave_type g_types[] = {
+	{
+		.name = "square",
+		.generator = square_wave_generator,
+	},
+	{
+		.name = "sine",
+		.generator = sine_wave_generator,
+	},
+	{
+		.name = "triangle",
+		.generator = triangle_wave_generator,
+	},
+	{
+		.name = "sawtooth",
+		.generator = sawtooth_wave_generator,
+	},
+	{ 0 }
+};
+
+#define BUFSIZE 4096
+#define UNSIGNED_MAX (~0U)
+
+int main(int argc, char *argv[])
+{
+	table_generator_func gen = square_wave_generator;
+	table_output_func out = stdout_c_header_table;
+	struct signal_spec spec = {
+		.type = DATA_TYPE_S16,
+		.wave = WAVE_TYPE_SQUARE,
+		.length = 512,
+	};
+	char buf[BUFSIZE];
+	unsigned k, count;
+	long tmp;
+	struct wave_type *ptr;
+	char *arg_wave, *arg_length, *arg_format;
+
+	if (argc < 4) {
+		printf("Usage: generate-wave-table <wave_type> <length> <format>\n");
+		printf("wave_types:\n");
+		for (ptr = g_types ; ptr->name != 0 ; ++ptr) {
+			printf("    %s\n", ptr->name);
+		}
+		printf("length: a positive integer\n");
+		printf("format: S16 or S32\n");
+		printf("All output is to stdout in a format suitable for C files.\n");
+		printf("Data generated is in the native byte order.\n");
+		return 0;
+	}
+
+	arg_wave = argv[1];
+	arg_length = argv[2];
+	arg_format = argv[3];
+
+	for (ptr = g_types ; ptr->name != NULL ; ++ptr) {
+		if (strcmp(arg_wave, ptr->name) == 0) {
+			spec.wave = ptr->wave_id;
+			gen = ptr->generator;
+			break;
+		}
+	}
+	if (ptr->name == NULL) {
+		fprintf(stderr, "Error: invalid wave type %s\n", arg_wave);
+		return 1;
+	}
+
+	tmp = atol(arg_length);
+	if (tmp < 8) {
+		fprintf(stderr, "Error: wave length must be at least 8 "
+			"samples\n");
+		return 1;
+	}
+	if (tmp > UINT_MAX) {
+		fprintf(stderr, "Error: wave length must fit into an "
+			"unsigned integer\n");
+		return 1;
+	}
+	spec.length = tmp;
+
+	if (0 == strcmp(arg_format, "S16")) {
+		spec.type = DATA_TYPE_S16;
+	} else if (0 == strcmp(arg_format, "S32")) {
+		spec.type = DATA_TYPE_S32;
+	} else {
+		fprintf(stderr, "Error: unsupported format %s "
+			"(must be 'S16' or 'S32')\n", arg_format);
+		return 1;
+	}
+
+	memset(buf, 0, sizeof(buf));
+	count = 0;
+	for (k=0 ; k<spec.length ; k += count) {
+		count = BUFSIZE;
+		switch (spec.type) {
+		case DATA_TYPE_S16:
+			count /= 2;
+			break;
+		case DATA_TYPE_S32:
+			count /= 4;
+			break;
+		default:
+			assert(0);
+		}
+		if (k + count > spec.length)
+			count = spec.length - k;
+		gen(&spec, k, count, buf);
+		out(buf, spec.type, count);
+	}
+
+	return 0;
+}
diff --git a/audio-tool/include/tinyalsa/asoundlib.h b/audio-tool/include/tinyalsa/asoundlib.h
new file mode 100644
index 0000000..1546220
--- /dev/null
+++ b/audio-tool/include/tinyalsa/asoundlib.h
@@ -0,0 +1,208 @@
+/* asoundlib.h
+**
+** Copyright 2011, The Android Open Source Project
+**
+** 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.
+**     * Neither the name of The Android Open Source Project nor the names of
+**       its contributors may be used to endorse or promote products derived
+**       from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``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 Android Open Source Project 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.
+*/
+
+#ifndef ASOUNDLIB_H
+#define ASOUNDLIB_H
+
+#include <sys/time.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * PCM API
+ */
+
+struct pcm;
+
+#define PCM_OUT        0x00000000
+#define PCM_IN         0x10000000
+#define PCM_MMAP       0x00000001
+#define PCM_NOIRQ      0x00000002
+#define PCM_NORESTART  0x00000004 /* PCM_NORESTART - when set, calls to
+                                   * pcm_write for a playback stream will not
+                                   * attempt to restart the stream in the case
+                                   * of an underflow, but will return -EPIPE
+                                   * instead.  After the first -EPIPE error, the
+                                   * stream is considered to be stopped, and a
+                                   * second call to pcm_write will attempt to
+                                   * restart the stream.
+                                   */
+
+/* PCM runtime states */
+#define	PCM_STATE_OPEN		0
+#define	PCM_STATE_SETUP		1
+#define	PCM_STATE_PREPARED	2
+#define	PCM_STATE_RUNNING		3
+#define	PCM_STATE_XRUN		4
+#define	PCM_STATE_DRAINING	5
+#define	PCM_STATE_PAUSED		6
+#define	PCM_STATE_SUSPENDED	7
+#define	PCM_STATE_DISCONNECTED	8
+
+/* Bit formats */
+enum pcm_format {
+    PCM_FORMAT_S16_LE = 0,
+    PCM_FORMAT_S32_LE,
+    PCM_FORMAT_S8,
+    PCM_FORMAT_S24_LE,
+    PCM_FORMAT_S24_3LE,
+
+    PCM_FORMAT_MAX,
+};
+
+/* Configuration for a stream */
+struct pcm_config {
+    unsigned int channels;
+    unsigned int rate;
+    unsigned int period_size;
+    unsigned int period_count;
+    enum pcm_format format;
+
+    /* Values to use for the ALSA start, stop and silence thresholds.  Setting
+     * any one of these values to 0 will cause the default tinyalsa values to be
+     * used instead.  Tinyalsa defaults are as follows.
+     *
+     * start_threshold   : period_count * period_size
+     * stop_threshold    : period_count * period_size
+     * silence_threshold : 0
+     */
+    unsigned int start_threshold;
+    unsigned int stop_threshold;
+    unsigned int silence_threshold;
+};
+
+/* Mixer control types */
+enum mixer_ctl_type {
+    MIXER_CTL_TYPE_BOOL,
+    MIXER_CTL_TYPE_INT,
+    MIXER_CTL_TYPE_ENUM,
+    MIXER_CTL_TYPE_BYTE,
+    MIXER_CTL_TYPE_IEC958,
+    MIXER_CTL_TYPE_INT64,
+    MIXER_CTL_TYPE_UNKNOWN,
+
+    MIXER_CTL_TYPE_MAX,
+};
+
+/* Open and close a stream */
+struct pcm *pcm_open(unsigned int card, unsigned int device,
+                     unsigned int flags, struct pcm_config *config);
+int pcm_close(struct pcm *pcm);
+int pcm_is_ready(struct pcm *pcm);
+
+/* Set and get config */
+int pcm_get_config(struct pcm *pcm, struct pcm_config *config);
+int pcm_set_config(struct pcm *pcm, struct pcm_config *config);
+
+/* Returns a human readable reason for the last error */
+const char *pcm_get_error(struct pcm *pcm);
+
+/* Returns the buffer size (int frames) that should be used for pcm_write. */
+unsigned int pcm_get_buffer_size(struct pcm *pcm);
+unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames);
+unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes);
+
+/* Returns the pcm latency in ms */
+unsigned int pcm_get_latency(struct pcm *pcm);
+
+/* Returns available frames in pcm buffer and corresponding time stamp.
+ * For an input stream, frames available are frames ready for the
+ * application to read.
+ * For an output stream, frames available are the number of empty frames available
+ * for the application to write.
+ */
+int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
+                       struct timespec *tstamp);
+
+/* Write data to the fifo.
+ * Will start playback on the first write or on a write that
+ * occurs after a fifo underrun.
+ */
+int pcm_write(struct pcm *pcm, const void *data, unsigned int count);
+int pcm_read(struct pcm *pcm, void *data, unsigned int count);
+
+/*
+ * mmap() support.
+ */
+int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count);
+int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset,
+                   unsigned int *frames);
+int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames);
+
+/* Start and stop a PCM channel that doesn't transfer data */
+int pcm_start(struct pcm *pcm);
+int pcm_stop(struct pcm *pcm);
+
+
+/*
+ * MIXER API
+ */
+
+struct mixer;
+struct mixer_ctl;
+
+/* Open and close a mixer */
+struct mixer *mixer_open(unsigned int card);
+void mixer_close(struct mixer *mixer);
+
+/* Obtain mixer controls */
+unsigned int mixer_get_num_ctls(struct mixer *mixer);
+struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id);
+struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name);
+
+/* Get info about mixer controls */
+const char *mixer_ctl_get_name(struct mixer_ctl *ctl);
+enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl);
+const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl);
+unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl);
+unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl);
+const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
+                                      unsigned int enum_id);
+
+/* Set and get mixer controls */
+int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id);
+int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent);
+
+int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id);
+int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value);
+int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string);
+
+/* Determe range of integer mixer controls */
+int mixer_ctl_get_range_min(struct mixer_ctl *ctl);
+int mixer_ctl_get_range_max(struct mixer_ctl *ctl);
+
+/* Interrupt driven API */
+int pcm_wait(struct pcm *pcm, int timeout);
+
+#if defined(__cplusplus)
+}  /* extern "C" */
+#endif
+
+#endif
diff --git a/audio-tool/main.c b/audio-tool/main.c
new file mode 100644
index 0000000..275c48c
--- /dev/null
+++ b/audio-tool/main.c
@@ -0,0 +1,117 @@
+/*
+ * audio-tool.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <libgen.h>
+
+#include "tinyplay.h"
+#include "tinycap.h"
+#include "tinymix.h"
+#include "pulse-generator.h"
+#include "tone-generator.h"
+#include "save.h"
+#include "restore.h"
+#include "defaults.h"
+#include "config_cmd.h"
+#include "wav_chan_splitter.h"
+#include "rms.h"
+
+/* defined in config.c */
+int parse_args(struct audio_tool_config *config, int *argc, char ***argv);
+void usage(void);
+
+int main(int argc, char* argv[])
+{
+	struct audio_tool_config config;
+	int ret;
+	int tinyplay = 0, tinycap = 0, tinymix = 0;
+
+	/* support backwards-compatible mode with the tinyutils */
+	if (strcmp(basename(argv[0]), "tinyplay") == 0) {
+		tinyplay = 1;
+	} else if (strcmp(basename(argv[0]), "tinycap") == 0) {
+		tinycap = 1;
+	} else if (strcmp(basename(argv[0]), "tinymix") == 0) {
+		tinymix = 1;
+	}
+
+	/* redefines argc and argv */
+	ret = parse_args(&config, &argc, &argv);
+
+	if (!ret && (argc | tinyplay | tinycap | tinymix)) {
+		/* Check for special modes first since argv[0] may be NULL */
+		if (tinyplay) {
+			ret = tinyplay_main(&config, argc, argv, tinyplay);
+		} else if (tinycap) {
+			ret = tinycap_main(&config, argc, argv, tinycap);
+		} else if (tinymix) {
+			ret = tinymix_main(&config, argc, argv, tinymix);
+
+		/* normal modes */
+		} else if (strcmp(argv[0], "play") == 0) {
+			ret = tinyplay_main(&config, argc, argv, tinyplay);
+		} else if (strcmp(argv[0], "cap") == 0
+			   || strcmp(argv[0], "capture") == 0) {
+			ret = tinycap_main(&config, argc, argv, tinycap);
+		} else if (strcmp(argv[0], "mix") == 0) {
+			ret = tinymix_main(&config, argc, argv, tinymix);
+		} else if (strcmp(argv[0], "pulse") == 0) {
+			ret = pulse_generator_main(&config, argc, argv);
+		} else if (strcmp(argv[0], "tone") == 0) {
+			ret = tone_generator_main(&config, argc, argv);
+		} else if (strcmp(argv[0], "save") == 0) {
+			ret = save_main(&config, argc, argv);
+		} else if (strcmp(argv[0], "restore") == 0) {
+			ret = restore_main(&config, argc, argv);
+		} else if (strcmp(argv[0], "defaults") == 0) {
+			ret = defaults_main(&config, argc, argv);
+		} else if (strcmp(argv[0], "config") == 0) {
+			ret = config_cmd_main(&config, argc, argv);
+		} else if (strcmp(argv[0], "split") == 0) {
+			ret = split_main(argc, argv);
+		} else if (strcmp(argv[0], "rms") == 0) {
+			ret = rms_main(argc, argv);
+		} else {
+			usage();
+			ret = 1;
+		}
+	}
+
+	return ret;
+}
diff --git a/audio-tool/mixer.c b/audio-tool/mixer.c
new file mode 100644
index 0000000..9475e03
--- /dev/null
+++ b/audio-tool/mixer.c
@@ -0,0 +1,413 @@
+/* mixer.c
+**
+** Copyright 2011, The Android Open Source Project
+**
+** 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.
+**     * Neither the name of The Android Open Source Project nor the names of
+**       its contributors may be used to endorse or promote products derived
+**       from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``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 Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <sys/ioctl.h>
+
+#include <linux/ioctl.h>
+#include <sys/ioctl.h>
+#define __force
+#define __bitwise
+#define __user
+#include <sound/asound.h>
+
+#include <tinyalsa/asoundlib.h>
+
+struct mixer_ctl {
+    struct mixer *mixer;
+    struct snd_ctl_elem_info *info;
+    char **ename;
+};
+
+struct mixer {
+    int fd;
+    struct snd_ctl_elem_info *info;
+    struct mixer_ctl *ctl;
+    unsigned int count;
+};
+
+void mixer_close(struct mixer *mixer)
+{
+    unsigned int n,m;
+
+    if (!mixer)
+        return;
+
+    if (mixer->fd >= 0)
+        close(mixer->fd);
+
+    if (mixer->ctl) {
+        for (n = 0; n < mixer->count; n++) {
+            if (mixer->ctl[n].ename) {
+                unsigned int max = mixer->ctl[n].info->value.enumerated.items;
+                for (m = 0; m < max; m++)
+                    free(mixer->ctl[n].ename[m]);
+                free(mixer->ctl[n].ename);
+            }
+        }
+        free(mixer->ctl);
+    }
+
+    if (mixer->info)
+        free(mixer->info);
+
+    free(mixer);
+
+    /* TODO: verify frees */
+}
+
+struct mixer *mixer_open(unsigned int card)
+{
+    struct snd_ctl_elem_list elist;
+    struct snd_ctl_elem_info tmp;
+    struct snd_ctl_elem_id *eid = NULL;
+    struct mixer *mixer = NULL;
+    unsigned int n, m;
+    int fd;
+    char fn[256];
+
+    snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
+    fd = open(fn, O_RDWR);
+    if (fd < 0)
+        return 0;
+
+    memset(&elist, 0, sizeof(elist));
+    if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
+        goto fail;
+
+    mixer = calloc(1, sizeof(*mixer));
+    if (!mixer)
+        goto fail;
+
+    mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
+    mixer->info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
+    if (!mixer->ctl || !mixer->info)
+        goto fail;
+
+    eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
+    if (!eid)
+        goto fail;
+
+    mixer->count = elist.count;
+    mixer->fd = fd;
+    elist.space = mixer->count;
+    elist.pids = eid;
+    if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
+        goto fail;
+
+    for (n = 0; n < mixer->count; n++) {
+        struct snd_ctl_elem_info *ei = mixer->info + n;
+        ei->id.numid = eid[n].numid;
+        if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
+            goto fail;
+        mixer->ctl[n].info = ei;
+        mixer->ctl[n].mixer = mixer;
+        if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
+            char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
+            if (!enames)
+                goto fail;
+            mixer->ctl[n].ename = enames;
+            for (m = 0; m < ei->value.enumerated.items; m++) {
+                memset(&tmp, 0, sizeof(tmp));
+                tmp.id.numid = ei->id.numid;
+                tmp.value.enumerated.item = m;
+                if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
+                    goto fail;
+                enames[m] = strdup(tmp.value.enumerated.name);
+                if (!enames[m])
+                    goto fail;
+            }
+        }
+    }
+
+    free(eid);
+    return mixer;
+
+fail:
+    /* TODO: verify frees in failure case */
+    if (eid)
+        free(eid);
+    if (mixer)
+        mixer_close(mixer);
+    else if (fd >= 0)
+        close(fd);
+    return 0;
+}
+
+unsigned int mixer_get_num_ctls(struct mixer *mixer)
+{
+    if (!mixer)
+        return 0;
+
+    return mixer->count;
+}
+
+struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
+{
+    if (mixer && (id < mixer->count))
+        return mixer->ctl + id;
+
+    return NULL;
+}
+
+struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
+{
+    unsigned int n;
+
+    if (!mixer)
+        return NULL;
+
+    for (n = 0; n < mixer->count; n++)
+        if (!strcmp(name, (char*) mixer->info[n].id.name))
+            return mixer->ctl + n;
+
+    return NULL;
+}
+
+const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
+{
+    if (!ctl)
+        return NULL;
+
+    return (const char *)ctl->info->id.name;
+}
+
+enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)
+{
+    if (!ctl)
+        return MIXER_CTL_TYPE_UNKNOWN;
+
+    switch (ctl->info->type) {
+    case SNDRV_CTL_ELEM_TYPE_BOOLEAN:    return MIXER_CTL_TYPE_BOOL;
+    case SNDRV_CTL_ELEM_TYPE_INTEGER:    return MIXER_CTL_TYPE_INT;
+    case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
+    case SNDRV_CTL_ELEM_TYPE_BYTES:      return MIXER_CTL_TYPE_BYTE;
+    case SNDRV_CTL_ELEM_TYPE_IEC958:     return MIXER_CTL_TYPE_IEC958;
+    case SNDRV_CTL_ELEM_TYPE_INTEGER64:  return MIXER_CTL_TYPE_INT64;
+    default:                             return MIXER_CTL_TYPE_UNKNOWN;
+    };
+}
+
+const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl)
+{
+    if (!ctl)
+        return "";
+
+    switch (ctl->info->type) {
+    case SNDRV_CTL_ELEM_TYPE_BOOLEAN:    return "BOOL";
+    case SNDRV_CTL_ELEM_TYPE_INTEGER:    return "INT";
+    case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
+    case SNDRV_CTL_ELEM_TYPE_BYTES:      return "BYTE";
+    case SNDRV_CTL_ELEM_TYPE_IEC958:     return "IEC958";
+    case SNDRV_CTL_ELEM_TYPE_INTEGER64:  return "INT64";
+    default:                             return "Unknown";
+    };
+}
+
+unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)
+{
+    if (!ctl)
+        return 0;
+
+    return ctl->info->count;
+}
+
+static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)
+{
+    int range;
+
+    if (percent > 100)
+        percent = 100;
+    else if (percent < 0)
+        percent = 0;
+
+    range = (ei->value.integer.max - ei->value.integer.min);
+
+    return ei->value.integer.min + (range * percent) / 100;
+}
+
+static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
+{
+    int range = (ei->value.integer.max - ei->value.integer.min);
+
+    if (range == 0)
+        return 0;
+
+    return ((value - ei->value.integer.min) / range) * 100;
+}
+
+int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id)
+{
+    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+        return -EINVAL;
+
+    return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id));
+}
+
+int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
+{
+    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+        return -EINVAL;
+
+    return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent));
+}
+
+int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
+{
+    struct snd_ctl_elem_value ev;
+    int ret;
+
+    if (!ctl || (id >= ctl->info->count))
+        return -EINVAL;
+
+    memset(&ev, 0, sizeof(ev));
+    ev.id.numid = ctl->info->id.numid;
+    ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
+    if (ret < 0)
+        return ret;
+
+    switch (ctl->info->type) {
+    case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+        return !!ev.value.integer.value[id];
+
+    case SNDRV_CTL_ELEM_TYPE_INTEGER:
+        return ev.value.integer.value[id];
+
+    case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+        return ev.value.enumerated.item[id];
+
+    case SNDRV_CTL_ELEM_TYPE_BYTES:
+        return ev.value.bytes.data[id];
+
+    default:
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
+{
+    struct snd_ctl_elem_value ev;
+    int ret;
+
+    if (!ctl || (id >= ctl->info->count))
+        return -EINVAL;
+
+    memset(&ev, 0, sizeof(ev));
+    ev.id.numid = ctl->info->id.numid;
+    ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
+    if (ret < 0)
+        return ret;
+
+    switch (ctl->info->type) {
+    case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+        ev.value.integer.value[id] = !!value;
+        break;
+
+    case SNDRV_CTL_ELEM_TYPE_INTEGER:
+        ev.value.integer.value[id] = value;
+        break;
+
+    case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+        ev.value.enumerated.item[id] = value;
+        break;
+
+    default:
+        return -EINVAL;
+    }
+
+    return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
+}
+
+int mixer_ctl_get_range_min(struct mixer_ctl *ctl)
+{
+    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+        return -EINVAL;
+
+    return ctl->info->value.integer.min;
+}
+
+int mixer_ctl_get_range_max(struct mixer_ctl *ctl)
+{
+    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+        return -EINVAL;
+
+    return ctl->info->value.integer.max;
+}
+
+unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)
+{
+    if (!ctl)
+        return 0;
+
+    return ctl->info->value.enumerated.items;
+}
+
+const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
+                                      unsigned int enum_id)
+{
+    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
+        (enum_id >= ctl->info->value.enumerated.items))
+        return NULL;
+
+    return (const char *)ctl->ename[enum_id];
+}
+
+int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
+{
+    unsigned int i, num_enums;
+    struct snd_ctl_elem_value ev;
+    int ret;
+
+    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED))
+        return -EINVAL;
+
+    num_enums = ctl->info->value.enumerated.items;
+    for (i = 0; i < num_enums; i++) {
+        if (!strcmp(string, ctl->ename[i])) {
+            memset(&ev, 0, sizeof(ev));
+            ev.value.enumerated.item[0] = i;
+            ev.id.numid = ctl->info->id.numid;
+            ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
+            if (ret < 0)
+                return ret;
+            return 0;
+        }
+    }
+
+    return -EINVAL;
+}
+
diff --git a/audio-tool/mixer_cache.c b/audio-tool/mixer_cache.c
new file mode 100644
index 0000000..a0ec65f
--- /dev/null
+++ b/audio-tool/mixer_cache.c
@@ -0,0 +1,224 @@
+/*
+ * mixer_cache.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <tinyalsa/asoundlib.h>
+
+#include "mixer_cache.h"
+
+int mixer_cache_init(struct audio_tool_mixer_cache *cache)
+{
+	if (!cache)
+		return EINVAL;
+
+	memset(cache, 0, sizeof(struct audio_tool_mixer_cache));
+
+	return 0;
+}
+
+void mixer_cache_deinit(struct audio_tool_mixer_cache *cache)
+{
+	if (!cache)
+		return;
+	if (!cache->ctrls)
+		return;
+	free(cache->ctrls);
+}
+
+int mixer_cache_populate(struct audio_tool_mixer_cache *cache, struct mixer *mixer)
+{
+	struct mixer_ctl *ctl;
+	struct audio_tool_mixer_control_info *cur;
+	const char* name;
+	size_t count, n, v;
+	int tmp;
+
+	if (!cache)
+		return EINVAL;
+
+	if (!mixer)
+		return EINVAL;
+
+	if (cache->ctrls)
+		free(cache->ctrls);
+
+	count = mixer_get_num_ctls(mixer);
+	cache->count = count;
+
+	if (count) {
+		cache->ctrls = calloc(count, sizeof(struct audio_tool_mixer_control_info));
+		if (! cache->ctrls)
+			return -ENOMEM;
+	}
+
+	for (n = 0, cur = cache->ctrls ; n < count ; ++n, ++cur) {
+		ctl = mixer_get_ctl(mixer, n);
+		if (!ctl)
+			return ENODEV;
+		name = mixer_ctl_get_name(ctl);
+		if (!name)
+			return ENODEV;
+
+		cur->id = n;
+		cur->type = mixer_ctl_get_type(ctl);
+		strcpy(cur->name, name);
+		cur->num_values = mixer_ctl_get_num_values(ctl);
+		if (cur->num_values > MAX_NUM_VALUES)
+			cur->num_values = MAX_NUM_VALUES;
+		for (v = 0 ; v < cur->num_values ; ++v) {
+			switch (cur->type) {
+			case MIXER_CTL_TYPE_BOOL:
+			case MIXER_CTL_TYPE_INT:
+				cur->value.integer[v] = mixer_ctl_get_value(ctl, v);
+				break;
+			case MIXER_CTL_TYPE_ENUM:
+				tmp = mixer_ctl_get_value(ctl, v);
+				name = mixer_ctl_get_enum_string(ctl, tmp);
+				strcpy(cur->value.enumerated[v], name);
+				break;
+			case MIXER_CTL_TYPE_BYTE:
+				cur->value.byte[v] = mixer_ctl_get_value(ctl, v);
+				break;
+			case MIXER_CTL_TYPE_INT64:
+				cur->value.integer64[v] = mixer_ctl_get_value(ctl, v);
+				break;
+			default:
+				(void)0;
+			}
+		}
+	}
+
+	return 0;
+}
+
+int mixer_cache_apply(struct audio_tool_mixer_cache *cache, struct mixer *mixer)
+{
+	struct audio_tool_mixer_control_info *cur;
+	struct mixer_ctl *ctl;
+	int n, k, ret=0;
+
+	for (n = 0 ; n < cache->count ; ++n) {
+		cur = &cache->ctrls[n];
+		ctl = mixer_get_ctl(mixer, n);
+		for (k = 0 ; k < cur->num_values ; ++k) {
+			switch (cur->type) {
+			case MIXER_CTL_TYPE_BOOL:
+			case MIXER_CTL_TYPE_INT:
+				mixer_ctl_set_value(ctl, k, cur->value.integer[k]);
+				break;
+			case MIXER_CTL_TYPE_ENUM:
+				mixer_ctl_set_enum_by_string(ctl, cur->value.enumerated[k]);
+				break;
+			case MIXER_CTL_TYPE_BYTE:
+				mixer_ctl_set_value(ctl, k, cur->value.byte[k]);
+				break;
+			case MIXER_CTL_TYPE_INT64:
+				mixer_ctl_set_value(ctl, k, cur->value.integer64[k]);
+				break;
+			default:
+				ret = 1;
+			}
+		}
+	}
+
+	return ret;
+}
+
+int mixer_cache_get_id_by_name(struct audio_tool_mixer_cache *cache, const char* name)
+{
+	struct audio_tool_mixer_control_info *cur;
+
+	if (!cache)
+		return -EINVAL;
+	if (!name)
+		return -EINVAL;
+
+	for (cur = cache->ctrls ; cur < (cache->ctrls + cache->count) ; ++cur) {
+		if (strcmp(cur->name, name) == 0)
+			return cur->id;
+	}
+
+	return -ENODEV;
+}
+
+void mixer_cache_reset_touch(struct audio_tool_mixer_cache *cache)
+{
+	struct audio_tool_mixer_control_info *cur;
+
+	if (!cache)
+		return;
+
+	for (cur = cache->ctrls ; cur < (cache->ctrls + cache->count) ; ++cur) {
+		cur->touch = 0;
+	}
+}
+
+void mixer_cache_touch(struct audio_tool_mixer_cache *cache, int id)
+{
+	if (!cache)
+		return;
+	if (id < 0)
+		return;
+	if (id > cache->count)
+		return;
+
+	cache->ctrls[id].touch = 1;
+}
+
+int mixer_cache_audit_touch(struct audio_tool_mixer_cache *cache, int verbose)
+{
+	struct audio_tool_mixer_control_info *cur;
+	int ret = 0;
+
+	if (!cache)
+		return EINVAL;
+
+	for (cur = cache->ctrls ; cur < (cache->ctrls + cache->count) ; ++cur) {
+		if (!cur->touch) {
+			ret = 1;
+			if (verbose) {
+				fprintf(stderr, "Warning: Control #%d (%s) was not touched\n",
+					cur->id, cur->name);
+			}
+		}
+	}
+
+	return ret;
+}
+
diff --git a/audio-tool/mixer_cache.h b/audio-tool/mixer_cache.h
new file mode 100644
index 0000000..6c2642d
--- /dev/null
+++ b/audio-tool/mixer_cache.h
@@ -0,0 +1,84 @@
+/*
+ * mixer_cache.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __AUDIO_TOOL_MIXER_CACHE_H__
+#define __AUDIO_TOOL_MIXER_CACHE_H__
+
+#include <stdint.h>
+#include <tinyalsa/asoundlib.h>
+
+/* In kernel this is typically 44 */
+#define AUDIO_TOOL_MIX_CTL_NAME_MAX 128
+
+struct audio_tool_mixer_control_info;
+struct audio_tool_mixer_cache;
+
+/* Returns 0 on success, errno on failure*/
+int mixer_cache_init(struct audio_tool_mixer_cache *cache);
+void mixer_cache_deinit(struct audio_tool_mixer_cache *cache);
+/* Returns 0 on success, errno on failure */
+int mixer_cache_populate(struct audio_tool_mixer_cache *cache, struct mixer *mixer);
+/* Returns control id (>=0), or negative errno on failure */
+int mixer_cache_get_id_by_name(struct audio_tool_mixer_cache *cache, const char* name);
+void mixer_cache_reset_touch(struct audio_tool_mixer_cache *cache);
+void mixer_cache_touch(struct audio_tool_mixer_cache *cache, int id);
+int mixer_cache_audit_touch(struct audio_tool_mixer_cache *cache, int verbose);
+int mixer_cache_apply(struct audio_tool_mixer_cache *cache, struct mixer *mixer);
+
+#define MAX_NUM_VALUES 8
+
+struct audio_tool_mixer_control_info {
+	unsigned id;
+	char name[AUDIO_TOOL_MIX_CTL_NAME_MAX];
+	enum mixer_ctl_type type;
+	unsigned num_values;
+	union {
+		long integer[128];
+		int64_t integer64[64];
+		char enumerated[MAX_NUM_VALUES][64];
+		unsigned char byte[512];
+	} value;
+
+	int touch; /* For use with full-list processing */
+};
+
+struct audio_tool_mixer_cache {
+	size_t count;
+	struct audio_tool_mixer_control_info *ctrls;
+};
+
+#endif /* __OMAP_AUDIO_TOOL_MIXER_CACHE_H__ */
+
diff --git a/audio-tool/module.c b/audio-tool/module.c
new file mode 100644
index 0000000..6439743
--- /dev/null
+++ b/audio-tool/module.c
@@ -0,0 +1,109 @@
+/*
+ * module.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "module.h"
+
+struct module_node {
+	struct module_node *next;
+	struct audio_tool_module *module;
+};
+
+struct module_node *g_module_list = 0;
+
+static void module_list_push_back(struct module_node *list, struct module_node *node)
+{
+	if (!list)
+		return;
+
+	while (list->next)
+		list = list->next;
+
+	list->next = node;
+}
+
+int audio_tool_module_register(struct audio_tool_module *mod)
+{
+	struct module_node *node;
+
+	if (!mod)
+		return EINVAL;
+
+	node = calloc(1, sizeof(struct module_node));
+
+	if (!node)
+		return ENOMEM;
+
+	node->module = mod;
+
+	if (!g_module_list) {
+		g_module_list = node;
+	} else {
+		module_list_push_back(g_module_list, node);
+	}
+
+	return 0;
+}
+
+struct audio_tool_module* audio_tool_get_module(int module_type,
+	const char* optional_name)
+{
+	struct module_node *node = g_module_list;
+
+	if (!module_type || module_type > AUDIO_TOOL_MOD_TYPE_MAX)
+		return NULL;
+
+	for ( ; node ; node = node->next ) {
+		if (node->module->type != module_type)
+			continue;
+
+		if (optional_name) {
+			if(!strcmp(node->module->name, optional_name))
+				break;
+		} else {
+			if (!node->module->probe())
+				break;
+		}
+	}
+
+	if (node)
+		return node->module;
+
+	return 0;
+}
diff --git a/audio-tool/module.h b/audio-tool/module.h
new file mode 100644
index 0000000..bd10f03
--- /dev/null
+++ b/audio-tool/module.h
@@ -0,0 +1,121 @@
+/*
+ * module.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __AUDIO_TOOL_MODULE_H__
+#define __AUDIO_TOOL_MODULE_H__
+
+struct audio_tool_module;
+struct audio_tool_card_module;
+struct audio_tool_command_module;
+struct audio_tool_mixer_cache;
+struct mixer;
+
+#ifndef __GNUC__
+#error The module interface requires GCCs constructor/destructor attributes.
+#endif
+
+#define __init __attribute__((constructor))
+#define __exit __attribute__((destructor))
+
+int audio_tool_module_register(struct audio_tool_module *mod);
+
+/* Board module access */
+struct audio_tool_module* audio_tool_get_module(int module_type,
+	const char* optional_name);
+
+/* Module types */
+#define AUDIO_TOOL_MOD_TYPE_ANY		0
+#define AUDIO_TOOL_MOD_TYPE_CARD	1
+#define AUDIO_TOOL_MOD_TYPE_COMMAND	2
+
+#define AUDIO_TOOL_MOD_TYPE_MAX		AUDIO_TOOL_MOD_TYPE_COMMAND
+
+/* The type and the name (together) make the module unique.
+ * However, uniqueness is not enforced.
+ */
+struct audio_tool_module {
+	int type;
+	const char* name;
+
+	/* Required: return non-zero if the module supports the current device */
+	int (*probe)(void);
+};
+
+#define AUDIO_DIRECTION_PLAYBACK	0
+#define AUDIO_DIRECTION_CAPTURE		1
+
+struct audio_tool_card_module {
+	int type;
+	const char* name;
+
+	/* Required: return zero if the module supports the current device */
+	int (*probe)(void);
+
+	/* Required: Takes an existing mixer_cache and populates
+	 * it with default values.  Returns 0 on success.
+	 */
+	int (*get_mixer_defaults)(struct audio_tool_mixer_cache *cache);
+
+	/* Required: Supply front-ends and back-ends this card supports.
+	 * direction: 0 for playback, 1 for capture
+	 * Returns: fes and bes point to static, null-terminated array of strings
+	 */
+	int (*get_fe_be_names)(int direction, char ***fes, char ***bes);
+
+	/* Required */
+	int (*config)(struct mixer *mixer, int direction, const char* fe,
+		const char* be, int enable, int *optional_port);
+};
+
+/* Module for implementing sub-commands
+ *
+ * The name is the same as the sub-command name.
+ */
+struct audio_tool_command_module {
+	int type;
+	const char* name;
+
+	/* Required: return zero if the module supports the current device */
+	int (*probe)(void);
+
+	/* Required: The main entry point to the command's function
+	 * argc and argv are passed directly from the command line.
+	 */
+	int (*main)(int argc, char* argv[]);
+};
+
+#endif /* __OMAP_AUDIO_TOOL_MODULE_H__ */
+
diff --git a/audio-tool/oscillator-table.c b/audio-tool/oscillator-table.c
new file mode 100644
index 0000000..b4dd044
--- /dev/null
+++ b/audio-tool/oscillator-table.c
@@ -0,0 +1,214 @@
+/* Copyright (c) 2011, Gabriel M. Beddingfield <gabrbedd@gmail.com>
+ * 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.
+ *     * Neither the name of the <organization> nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 <COPYRIGHT HOLDER> 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.
+ */
+
+/*
+ * oscillator-table.c
+ *
+ * Utilities generating an accurate waveform to an audio output.
+ */
+
+#include <assert.h>
+#include <math.h>
+#include <stdint.h>
+#include <limits.h>
+
+#include "oscillator-table.h"
+
+/*#define CHECK_MATH*/
+
+typedef void (*write_to_buffer_func_t)(void *out, uint16_t frame, uint8_t channels,
+				       uint8_t channel, int16_t value);
+
+void write_s16_to_s8(void *out, uint16_t frame, uint8_t channels,
+				       uint8_t channel, int16_t value)
+{
+	int8_t *dest = out;
+	dest[frame * channels + channel] = value >> 8;
+}
+
+void write_s16_to_s16(void *out, uint16_t frame, uint8_t channels,
+				       uint8_t channel, int16_t value)
+{
+	int16_t *dest = out;
+	dest[frame * channels + channel] = value;
+}
+
+void write_s16_to_s24(void *out, uint16_t frame, uint8_t channels,
+				       uint8_t channel, int16_t value)
+{
+	union s24_t {
+		int32_t v;
+		unsigned char c[4];
+	} val;
+	int64_t tmp = value;
+	unsigned char *d;
+
+	/* scale 0x7FFF to 0x7FFFFF */
+	tmp = (tmp * 0x7FFFFF) / 0x7FFF;
+	assert( tmp <= (int64_t)0x7FFFFF );
+	assert( (tmp >= 0) || (-tmp <= (int64_t)0x7FFFFF ) );
+	val.v = tmp;
+	d = out;
+	d += 3 * (frame * channels + channel);
+	d[0] = val.c[0];
+	d[1] = val.c[1];
+	d[2] = val.c[2];
+}
+
+void write_s16_to_s32(void *out, uint16_t frame, uint8_t channels,
+				       uint8_t channel, int16_t value)
+{
+	int32_t *dest = out;
+	int64_t tmp = value;
+	int32_t v;
+
+	/* scale 0x7FFF to 0x7FFFFFFF */
+	tmp = (tmp * 0x7FFFFFFF) / 0x7FFF;
+	assert( tmp <= (int64_t)0x7FFFFFFF );
+	assert( (tmp >= 0) || (-tmp <= (int64_t)0x7FFFFFFF ) );
+	v = tmp;
+	dest[frame * channels + channel] = v;
+}
+
+/**
+ * \brief render an oscillator table to an output buffer, using the freq specified
+ *
+ * \param out The output buffer (PCM16)
+ * \param tbl The source oscillator table
+ * \param offset the frame offset of the wave as rendered on the output
+ * \param count the number of frames to render this cycle
+ * \param wave_scale specification of the desired output wavelength
+ * \param channels number of output channels in the buffer (range: [1,32])
+ * \param clannel_mask mask indicating which channels to write to
+ * \param vol_frac An integer fraction for attenuating the signal
+ * \param format output bits (should be 8, 16, 24, or 32)
+ *
+ * \returns non-zero on error.
+ */
+int oscillator_table_render(void *out, struct wave_table *tbl, uint32_t offset,
+		uint16_t count, const struct wave_scale wave_scale, uint8_t channels,
+		uint32_t channel_mask, uint16_t vol_frac, int bits)
+{
+	int32_t num;
+	const int32_t denom = USHRT_MAX;
+	uint32_t tbl_len, wave_len;
+	uint32_t k, frame, f_reg, r_reg;
+	uint32_t p;
+	int16_t val;
+	int32_t a, b;
+	uint8_t ch;
+	int32_t chbit;
+	char interpolate = 1;
+	int16_t shift;
+	uint32_t f;
+#ifdef CHECK_MATH
+	uint32_t ck_p, ck_tbl_len, ck_wave_len;
+#endif
+	write_to_buffer_func_t write_to_buffer;
+
+	assert( out );
+	assert( tbl );
+	assert( channels > 0 );
+	assert( 0 == (bits % 8) );
+	assert( bits > 0 );
+	assert( bits <= 32 );
+
+	switch (bits) {
+	case 8: write_to_buffer = write_s16_to_s8; break;
+	case 16: write_to_buffer = write_s16_to_s16; break;
+	case 24: write_to_buffer = write_s16_to_s24; break;
+	case 32: write_to_buffer = write_s16_to_s32; break;
+	}
+
+	num = vol_frac;
+	tbl_len = tbl->length << wave_scale.sub_shift;
+	wave_len = (wave_scale.length << wave_scale.sub_shift) | wave_scale.sub;
+#ifdef CHECK_MATH
+	ck_tbl_len = (uint64_t)tbl->length << wave_scale.sub_shift;
+	ck_wave_len = ((uint64_t)wave_scale.length << wave_scale.sub_shift) | wave_scale.sub;
+#endif
+	if ( (tbl_len / wave_len) > 4 )
+		interpolate = 0;
+
+	for (k=0 ; k < count ; ++k) {
+		/* This bit of code is over-optimized for 32-bit processors.
+		 * See the "CHECK_MATH" section for grokkable code.
+		 * Why?  Because it was a fun challenge.
+		 *
+		 * Strategy: Break "frame" into 8-bit chunks, and build
+		 * up "p", being careful to preserve remainders.
+		 */
+		assert( (1<<16) > tbl->mask ); /* uncomment code if this fails */
+		assert( wave_len == (wave_len & 0xFFFFFF) );
+		assert( tbl_len == (tbl_len & 0xFFFFFF) );
+		frame = offset + k;
+		p = 0;
+		f_reg = 0;
+		r_reg = 0;
+		for (shift=24 ; shift >= 0 ; shift -= 8) {
+			f = (frame >> shift) & 0xFF;
+			f_reg = f * tbl_len;
+			r_reg = (r_reg << 8) + f_reg % wave_len;
+			p = (p << 8) + f_reg / wave_len + r_reg / wave_len;
+			r_reg %= wave_len;
+		}
+		p &= tbl->mask;
+#ifdef CHECK_MATH
+		ck_p = ((uint64_t)(offset + k) * ck_tbl_len) / ck_wave_len;
+		ck_p &= tbl->mask;
+		assert( p == ck_p );
+#endif
+		if (interpolate) {
+			/* interpolate */
+			int32_t water;
+			a = tbl->data[p];
+			b = tbl->data[p + 1];
+			assert( (int32_t)r_reg >= 0 );
+			assert( (int32_t)wave_len >= 0 );
+			/* water down r_reg and wave_len if they will overflow */
+			water = 4;
+			while ((r_reg >> water) > 0x7FFF)
+				++water;
+			val = a + ((b-a) * ((int32_t)r_reg >> water)) / ((int32_t)wave_len >> water);
+			if (a < b)
+				assert( a <= val && val <= b );
+			else
+				assert( a >= val && val >= b );
+		} else {
+			val = tbl->data[p];
+		}
+		val = (val * num) / denom;
+		for (ch = 0, chbit=1 ; ch < channels ; ++ch, chbit <<= 1) {
+			if ( channel_mask & chbit )
+				write_to_buffer(out, k, channels, ch, val);
+			else
+				write_to_buffer(out, k, channels, ch, 0);
+		}
+	}
+
+	return 0;
+}
+
diff --git a/audio-tool/oscillator-table.h b/audio-tool/oscillator-table.h
new file mode 100644
index 0000000..6bb1a71
--- /dev/null
+++ b/audio-tool/oscillator-table.h
@@ -0,0 +1,61 @@
+/* Copyright (c) 2012, Gabriel M. Beddingfield <gabrbedd@gmail.com>
+ * 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.
+ *     * Neither the name of the <organization> nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 <COPYRIGHT HOLDER> 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.
+ */
+#ifndef __LIBGABE_OSCILLATOR_TABLE_H__
+#define __LIBGABE_OSCILLATOR_TABLE_H__
+
+#define IS_POWER_OF_TWO(x) ((0) == ((x) & ((x)-1)))
+
+struct wave_table {
+	const char* name;
+	uint16_t length; /* Must be a power of two */
+	uint16_t mask;   /* = length - 1 */
+	int16_t *data;
+};
+
+struct wave_scale {
+	uint16_t length;
+	uint16_t sub;       /* sub-frames, binary fraction with
+			       denominator = ((1 << sub_shift) - 1)*/
+	uint16_t sub_den;   /* must be (power of 2)-1 */
+	uint16_t sub_shift; /* denominator shift, sub_den = (1 << sub_shift) - 1 */
+};
+
+#define STATIC_ARRAY_SIZE(ra) (sizeof(ra)/sizeof(ra[0]))
+
+#define DECLARE_TABLE(t_name, t_data)					\
+	{								\
+		.name = (t_name),					\
+		.length = STATIC_ARRAY_SIZE(t_data),			\
+		.mask = STATIC_ARRAY_SIZE(t_data) - 1,			\
+		.data = (t_data),					\
+	}
+
+int oscillator_table_render(void *out, struct wave_table *tbl, uint32_t offset,
+		uint16_t count, const struct wave_scale wave_scale, uint8_t channels,
+		uint32_t channel_mask, uint16_t vol_frac, int bits);
+
+#endif /* __LIBGABE_OSCILLATOR_TABLE_H__ */
+
diff --git a/audio-tool/pcm.c b/audio-tool/pcm.c
new file mode 100644
index 0000000..758e4b1
--- /dev/null
+++ b/audio-tool/pcm.c
@@ -0,0 +1,837 @@
+/* pcm.c
+**
+** Copyright 2011, The Android Open Source Project
+**
+** 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.
+**     * Neither the name of The Android Open Source Project nor the names of
+**       its contributors may be used to endorse or promote products derived
+**       from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``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 Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <poll.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <limits.h>
+
+#include <linux/ioctl.h>
+#define __force
+#define __bitwise
+#define __user
+#include <sound/asound.h>
+
+#include <tinyalsa/asoundlib.h>
+
+#define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL
+#define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2)
+
+static inline int param_is_mask(int p)
+{
+    return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+        (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
+}
+
+static inline int param_is_interval(int p)
+{
+    return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) &&
+        (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL);
+}
+
+static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n)
+{
+    return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
+{
+    return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit)
+{
+    if (bit >= SNDRV_MASK_MAX)
+        return;
+    if (param_is_mask(n)) {
+        struct snd_mask *m = param_to_mask(p, n);
+        m->bits[0] = 0;
+        m->bits[1] = 0;
+        m->bits[bit >> 5] |= (1 << (bit & 31));
+    }
+}
+
+static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val)
+{
+    if (param_is_interval(n)) {
+        struct snd_interval *i = param_to_interval(p, n);
+        i->min = val;
+    }
+}
+
+static void param_set_max(struct snd_pcm_hw_params *p, int n, unsigned int val)
+{
+    if (param_is_interval(n)) {
+        struct snd_interval *i = param_to_interval(p, n);
+        i->max = val;
+    }
+}
+
+static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val)
+{
+    if (param_is_interval(n)) {
+        struct snd_interval *i = param_to_interval(p, n);
+        i->min = val;
+        i->max = val;
+        i->integer = 1;
+    }
+}
+
+static unsigned int param_get_int(struct snd_pcm_hw_params *p, int n)
+{
+    if (param_is_interval(n)) {
+        struct snd_interval *i = param_to_interval(p, n);
+        if (i->integer)
+            return i->max;
+    }
+    return 0;
+}
+
+static void param_init(struct snd_pcm_hw_params *p)
+{
+    int n;
+
+    memset(p, 0, sizeof(*p));
+    for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK;
+         n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) {
+            struct snd_mask *m = param_to_mask(p, n);
+            m->bits[0] = ~0;
+            m->bits[1] = ~0;
+    }
+    for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
+         n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) {
+            struct snd_interval *i = param_to_interval(p, n);
+            i->min = 0;
+            i->max = ~0;
+    }
+}
+
+#define PCM_ERROR_MAX 128
+
+struct pcm {
+    int fd;
+    unsigned int flags;
+    int running:1;
+    int underruns;
+    unsigned int buffer_size;
+    unsigned int boundary;
+    char error[PCM_ERROR_MAX];
+    struct pcm_config config;
+    struct snd_pcm_mmap_status *mmap_status;
+    struct snd_pcm_mmap_control *mmap_control;
+    struct snd_pcm_sync_ptr *sync_ptr;
+    void *mmap_buffer;
+    unsigned int noirq_frames_per_msec;
+};
+
+unsigned int pcm_get_buffer_size(struct pcm *pcm)
+{
+    return pcm->buffer_size;
+}
+
+const char* pcm_get_error(struct pcm *pcm)
+{
+    return pcm->error;
+}
+
+static int oops(struct pcm *pcm, int e, const char *fmt, ...)
+{
+    va_list ap;
+    int sz;
+
+    va_start(ap, fmt);
+    vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap);
+    va_end(ap);
+    sz = strlen(pcm->error);
+
+    if (errno)
+        snprintf(pcm->error + sz, PCM_ERROR_MAX - sz,
+                 ": %s", strerror(e));
+    return -1;
+}
+
+static unsigned int pcm_format_to_alsa(enum pcm_format format)
+{
+    switch (format) {
+    case PCM_FORMAT_S32_LE:
+        return SNDRV_PCM_FORMAT_S32_LE;
+    case PCM_FORMAT_S8:
+        return SNDRV_PCM_FORMAT_S8;
+    case PCM_FORMAT_S24_LE:
+        return SNDRV_PCM_FORMAT_S24_LE;
+    case PCM_FORMAT_S24_3LE:
+        return SNDRV_PCM_FORMAT_S24_3LE;
+    default:
+    case PCM_FORMAT_S16_LE:
+        return SNDRV_PCM_FORMAT_S16_LE;
+    };
+}
+
+static unsigned int pcm_format_to_bits(enum pcm_format format)
+{
+    switch (format) {
+    case PCM_FORMAT_S32_LE:
+    case PCM_FORMAT_S24_LE:
+        return 32;
+    case PCM_FORMAT_S24_3LE:
+        return 24;
+    default:
+    case PCM_FORMAT_S16_LE:
+        return 16;
+    };
+}
+
+unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes)
+{
+    return bytes / (pcm->config.channels *
+        (pcm_format_to_bits(pcm->config.format) >> 3));
+}
+
+unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames)
+{
+    return frames * pcm->config.channels *
+        (pcm_format_to_bits(pcm->config.format) >> 3);
+}
+
+static int pcm_sync_ptr(struct pcm *pcm, int flags) {
+    if (pcm->sync_ptr) {
+        pcm->sync_ptr->flags = flags;
+        if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0)
+            return -1;
+    }
+    return 0;
+}
+
+static int pcm_hw_mmap_status(struct pcm *pcm) {
+
+    if (pcm->sync_ptr)
+        return 0;
+
+    int page_size = sysconf(_SC_PAGE_SIZE);
+    pcm->mmap_status = mmap(NULL, page_size, PROT_READ, MAP_FILE | MAP_SHARED,
+                            pcm->fd, SNDRV_PCM_MMAP_OFFSET_STATUS);
+    if (pcm->mmap_status == MAP_FAILED)
+        pcm->mmap_status = NULL;
+    if (!pcm->mmap_status)
+        goto mmap_error;
+
+    pcm->mmap_control = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
+                             MAP_FILE | MAP_SHARED, pcm->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL);
+    if (pcm->mmap_control == MAP_FAILED)
+        pcm->mmap_control = NULL;
+    if (!pcm->mmap_control) {
+        munmap(pcm->mmap_status, page_size);
+        pcm->mmap_status = NULL;
+        goto mmap_error;
+    }
+    pcm->mmap_control->avail_min = 1;
+
+    return 0;
+
+mmap_error:
+
+    pcm->sync_ptr = calloc(1, sizeof(*pcm->sync_ptr));
+    if (!pcm->sync_ptr)
+        return -ENOMEM;
+    pcm->mmap_status = &pcm->sync_ptr->s.status;
+    pcm->mmap_control = &pcm->sync_ptr->c.control;
+    pcm->mmap_control->avail_min = 1;
+    pcm_sync_ptr(pcm, 0);
+
+    return 0;
+}
+
+static void pcm_hw_munmap_status(struct pcm *pcm) {
+    if (pcm->sync_ptr) {
+        free(pcm->sync_ptr);
+        pcm->sync_ptr = NULL;
+    } else {
+        int page_size = sysconf(_SC_PAGE_SIZE);
+        if (pcm->mmap_status)
+            munmap(pcm->mmap_status, page_size);
+        if (pcm->mmap_control)
+            munmap(pcm->mmap_control, page_size);
+    }
+    pcm->mmap_status = NULL;
+    pcm->mmap_control = NULL;
+}
+
+static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset,
+                          const char *src, unsigned int src_offset,
+                          unsigned int frames)
+{
+    int size_bytes = pcm_frames_to_bytes(pcm, frames);
+    int pcm_offset_bytes = pcm_frames_to_bytes(pcm, pcm_offset);
+    int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset);
+
+    /* interleaved only atm */
+    memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes,
+           src + src_offset_bytes, size_bytes);
+    return 0;
+}
+
+static int pcm_mmap_write_areas(struct pcm *pcm, const char *src,
+                                unsigned int offset, unsigned int size)
+{
+    void *pcm_areas;
+    int commit;
+    unsigned int pcm_offset, frames, count = 0;
+
+    while (size > 0) {
+        frames = size;
+        pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
+        pcm_areas_copy(pcm, pcm_offset, src, offset, frames);
+        commit = pcm_mmap_commit(pcm, pcm_offset, frames);
+        if (commit < 0) {
+            oops(pcm, commit, "failed to commit %d frames\n", frames);
+            return commit;
+        }
+
+        offset += commit;
+        count += commit;
+        size -= commit;
+    }
+    return count;
+}
+
+int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
+                       struct timespec *tstamp)
+{
+    int frames;
+    int rc;
+    snd_pcm_uframes_t hw_ptr;
+
+    if (!pcm_is_ready(pcm))
+        return -1;
+
+    rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC);
+    if (rc < 0)
+        return -1;
+
+    if ((pcm->mmap_status->state != PCM_STATE_RUNNING) &&
+            (pcm->mmap_status->state != PCM_STATE_DRAINING))
+        return -1;
+
+    *tstamp = pcm->mmap_status->tstamp;
+    if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0)
+        return -1;
+
+    hw_ptr = pcm->mmap_status->hw_ptr;
+    if (pcm->flags & PCM_IN)
+        frames = hw_ptr - pcm->mmap_control->appl_ptr;
+    else
+        frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
+
+    if (frames < 0)
+        return -1;
+
+    *avail = (unsigned int)frames;
+
+    return 0;
+}
+
+int pcm_write(struct pcm *pcm, const void *data, unsigned int count)
+{
+    struct snd_xferi x;
+
+    if (pcm->flags & PCM_IN)
+        return -EINVAL;
+
+    x.buf = (void*)data;
+    x.frames = count / (pcm->config.channels *
+                        pcm_format_to_bits(pcm->config.format) / 8);
+
+    for (;;) {
+        if (!pcm->running) {
+            if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE))
+                return oops(pcm, errno, "cannot prepare channel");
+            if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x))
+                return oops(pcm, errno, "cannot write initial data");
+            pcm->running = 1;
+            return 0;
+        }
+        if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
+            pcm->running = 0;
+            if (errno == EPIPE) {
+                /* we failed to make our window -- try to restart if we are
+                 * allowed to do so.  Otherwise, simply allow the EPIPE error to
+                 * propagate up to the app level */
+                pcm->underruns++;
+                if (pcm->flags & PCM_NORESTART)
+                    return -EPIPE;
+                continue;
+            }
+            return oops(pcm, errno, "cannot write stream data");
+        }
+        return 0;
+    }
+}
+
+int pcm_read(struct pcm *pcm, void *data, unsigned int count)
+{
+    struct snd_xferi x;
+
+    if (!(pcm->flags & PCM_IN))
+        return -EINVAL;
+
+    x.buf = data;
+    x.frames = count / (pcm->config.channels *
+                        pcm_format_to_bits(pcm->config.format) / 8);
+
+    for (;;) {
+        if (!pcm->running) {
+            if (pcm_start(pcm) < 0) {
+                fprintf(stderr, "start error");
+                return -errno;
+            }
+        }
+        if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) {
+            pcm->running = 0;
+            if (errno == EPIPE) {
+                    /* we failed to make our window -- try to restart */
+                pcm->underruns++;
+                continue;
+            }
+            return oops(pcm, errno, "cannot read stream data");
+        }
+        return 0;
+    }
+}
+
+static struct pcm bad_pcm = {
+    .fd = -1,
+};
+
+int pcm_close(struct pcm *pcm)
+{
+    if (pcm == &bad_pcm)
+        return 0;
+
+    pcm_hw_munmap_status(pcm);
+
+    if (pcm->flags & PCM_MMAP) {
+        pcm_stop(pcm);
+        munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
+    }
+
+    if (pcm->fd >= 0)
+        close(pcm->fd);
+    pcm->running = 0;
+    pcm->buffer_size = 0;
+    pcm->fd = -1;
+    free(pcm);
+    return 0;
+}
+
+struct pcm *pcm_open(unsigned int card, unsigned int device,
+                     unsigned int flags, struct pcm_config *config)
+{
+    struct pcm *pcm;
+    struct snd_pcm_info info;
+    struct snd_pcm_hw_params params;
+    struct snd_pcm_sw_params sparams;
+    char fn[256];
+    int rc;
+
+    pcm = calloc(1, sizeof(struct pcm));
+    if (!pcm || !config)
+        return &bad_pcm; /* TODO: could support default config here */
+
+    pcm->config = *config;
+
+    snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
+             flags & PCM_IN ? 'c' : 'p');
+
+    pcm->flags = flags;
+    pcm->fd = open(fn, O_RDWR);
+    if (pcm->fd < 0) {
+        oops(pcm, errno, "cannot open device '%s'", fn);
+        return pcm;
+    }
+
+    if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) {
+        oops(pcm, errno, "cannot get info");
+        goto fail_close;
+    }
+
+    param_init(&params);
+    param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT,
+                   pcm_format_to_alsa(config->format));
+    param_set_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
+                   SNDRV_PCM_SUBFORMAT_STD);
+    param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
+    param_set_int(&params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+                  pcm_format_to_bits(config->format));
+    param_set_int(&params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
+                  pcm_format_to_bits(config->format) * config->channels);
+    param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS,
+                  config->channels);
+    param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
+    param_set_int(&params, SNDRV_PCM_HW_PARAM_RATE, config->rate);
+
+    if (flags & PCM_NOIRQ) {
+
+        if (!(flags & PCM_MMAP)) {
+            oops(pcm, -EINVAL, "noirq only currently supported with mmap().");
+            goto fail;
+        }
+
+        params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP;
+        pcm->noirq_frames_per_msec = config->rate / 1000;
+    }
+
+    if (flags & PCM_MMAP)
+        param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
+                   SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
+    else
+        param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
+                   SNDRV_PCM_ACCESS_RW_INTERLEAVED);
+
+    if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) {
+        oops(pcm, errno, "cannot set hw params");
+        goto fail_close;
+    }
+
+    /* get our refined hw_params */
+    config->period_size = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+    config->period_count = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIODS);
+    pcm->buffer_size = config->period_count * config->period_size;
+
+    if (flags & PCM_MMAP) {
+        pcm->mmap_buffer = mmap(NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size),
+                                PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, pcm->fd, 0);
+        if (pcm->mmap_buffer == MAP_FAILED) {
+            oops(pcm, -errno, "failed to mmap buffer %d bytes\n",
+                 pcm_frames_to_bytes(pcm, pcm->buffer_size));
+            goto fail_close;
+        }
+    }
+
+
+    memset(&sparams, 0, sizeof(sparams));
+    sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
+    sparams.period_step = 1;
+    sparams.avail_min = 1;
+
+    if (!config->start_threshold)
+        pcm->config.start_threshold = sparams.start_threshold =
+            config->period_count * config->period_size / 2;
+    else
+        sparams.start_threshold = config->start_threshold;
+
+    /* pick a high stop threshold - todo: does this need further tuning */
+    if (!config->stop_threshold) {
+        if (pcm->flags & PCM_IN)
+            pcm->config.stop_threshold = sparams.stop_threshold =
+                config->period_count * config->period_size * 10;
+        else
+            pcm->config.stop_threshold = sparams.stop_threshold =
+                config->period_count * config->period_size;
+    }
+    else
+        sparams.stop_threshold = config->stop_threshold;
+
+    sparams.xfer_align = config->period_size / 2; /* needed for old kernels */
+    sparams.silence_size = 0;
+    sparams.silence_threshold = config->silence_threshold;
+    pcm->boundary = sparams.boundary = pcm->buffer_size;
+
+    while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size)
+		pcm->boundary *= 2;
+
+    if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
+        oops(pcm, errno, "cannot set sw params");
+        goto fail;
+    }
+
+    rc = pcm_hw_mmap_status(pcm);
+    if (rc < 0) {
+        oops(pcm, rc, "mmap status failed");
+        goto fail;
+    }
+
+    pcm->underruns = 0;
+    return pcm;
+
+fail:
+    if (flags & PCM_MMAP)
+        munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
+fail_close:
+    close(pcm->fd);
+    pcm->fd = -1;
+    return pcm;
+}
+
+int pcm_is_ready(struct pcm *pcm)
+{
+    return pcm->fd >= 0;
+}
+
+int pcm_start(struct pcm *pcm)
+{
+    if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0)
+        return oops(pcm, errno, "cannot prepare channel");
+
+    if (pcm->flags & PCM_MMAP)
+	    pcm_sync_ptr(pcm, 0);
+
+    if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START) < 0)
+        return oops(pcm, errno, "cannot start channel");
+
+    pcm->running = 1;
+    return 0;
+}
+
+int pcm_stop(struct pcm *pcm)
+{
+    if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0)
+        return oops(pcm, errno, "cannot stop channel");
+
+    pcm->running = 0;
+    return 0;
+}
+
+static inline int pcm_mmap_playback_avail(struct pcm *pcm)
+{
+    int avail;
+
+    avail = pcm->mmap_status->hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
+
+    if (avail < 0)
+        avail += pcm->boundary;
+    else if (avail > (int)pcm->boundary)
+        avail -= pcm->boundary;
+
+    return avail;
+}
+
+static inline int pcm_mmap_capture_avail(struct pcm *pcm)
+{
+    int avail = pcm->mmap_status->hw_ptr - pcm->mmap_control->appl_ptr;
+    if (avail < 0)
+        avail += pcm->boundary;
+    return avail;
+}
+
+static inline int pcm_mmap_avail(struct pcm *pcm)
+{
+    pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
+    if (pcm->flags & PCM_IN)
+        return pcm_mmap_capture_avail(pcm);
+    else
+        return pcm_mmap_playback_avail(pcm);
+}
+
+static void pcm_mmap_appl_forward(struct pcm *pcm, int frames)
+{
+    unsigned int appl_ptr = pcm->mmap_control->appl_ptr;
+    appl_ptr += frames;
+
+    /* check for boundary wrap */
+    if (appl_ptr > pcm->boundary)
+         appl_ptr -= pcm->boundary;
+    pcm->mmap_control->appl_ptr = appl_ptr;
+}
+
+int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset,
+                   unsigned int *frames)
+{
+    unsigned int continuous, copy_frames, avail;
+
+    /* return the mmap buffer */
+    *areas = pcm->mmap_buffer;
+
+    /* and the application offset in frames */
+    *offset = pcm->mmap_control->appl_ptr % pcm->buffer_size;
+
+    avail = pcm_mmap_avail(pcm);
+    if (avail > pcm->buffer_size)
+        avail = pcm->buffer_size;
+    continuous = pcm->buffer_size - *offset;
+
+    /* we can only copy frames if the are availabale and continuos */
+    copy_frames = *frames;
+    if (copy_frames > avail)
+        copy_frames = avail;
+    if (copy_frames > continuous)
+        copy_frames = continuous;
+    *frames = copy_frames;
+
+    return 0;
+}
+
+int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames)
+{
+    /* update the application pointer in userspace and kernel */
+    pcm_mmap_appl_forward(pcm, frames);
+    pcm_sync_ptr(pcm, 0);
+
+    return frames;
+}
+
+int pcm_avail_update(struct pcm *pcm)
+{
+    pcm_sync_ptr(pcm, 0);
+    return pcm_mmap_avail(pcm);
+}
+
+int pcm_state(struct pcm *pcm)
+{
+    int err = pcm_sync_ptr(pcm, 0);
+    if (err < 0)
+        return err;
+
+    return pcm->mmap_status->state;
+}
+
+int pcm_wait(struct pcm *pcm, int timeout)
+{
+    struct pollfd pfd;
+    int err;
+
+    pfd.fd = pcm->fd;
+    pfd.events = POLLOUT | POLLERR | POLLNVAL;
+
+    do {
+        /* let's wait for avail or timeout */
+        err = poll(&pfd, 1, timeout);
+        if (err < 0)
+            return -errno;
+
+        /* timeout ? */
+        if (err == 0)
+            return 0;
+
+        /* have we been interrupted ? */
+        if (errno == -EINTR)
+            continue;
+
+        /* check for any errors */
+        if (pfd.revents & (POLLERR | POLLNVAL)) {
+            switch (pcm_state(pcm)) {
+            case PCM_STATE_XRUN:
+                return -EPIPE;
+            case PCM_STATE_SUSPENDED:
+                return -ESTRPIPE;
+            case PCM_STATE_DISCONNECTED:
+                return -ENODEV;
+            default:
+                return -EIO;
+            }
+        }
+    /* poll again if fd not ready for IO */
+    } while (!(pfd.revents & (POLLIN | POLLOUT)));
+
+    return 1;
+}
+
+int pcm_mmap_write(struct pcm *pcm, const void *buffer, unsigned int bytes)
+{
+    int err = 0, frames, avail;
+    unsigned int offset = 0, count;
+
+    if (bytes == 0)
+        return 0;
+
+    count = pcm_bytes_to_frames(pcm, bytes);
+
+    while (count > 0) {
+
+        /* get the available space for writing new frames */
+        avail = pcm_avail_update(pcm);
+        if (avail < 0) {
+            fprintf(stderr, "cannot determine available mmap frames");
+            return err;
+        }
+
+        /* start the audio if we reach the threshold */
+	    if (!pcm->running &&
+            (pcm->buffer_size - avail) >= pcm->config.start_threshold) {
+            if (pcm_start(pcm) < 0) {
+               fprintf(stderr, "start error: hw 0x%x app 0x%x avail 0x%x\n",
+                    (unsigned int)pcm->mmap_status->hw_ptr,
+                    (unsigned int)pcm->mmap_control->appl_ptr,
+                    avail);
+                return -errno;
+            }
+        }
+
+        /* sleep until we have space to write new frames */
+        if (pcm->running &&
+            (unsigned int)avail < pcm->mmap_control->avail_min) {
+            int time = -1;
+
+            if (pcm->flags & PCM_NOIRQ)
+                time = (pcm->buffer_size - avail - pcm->mmap_control->avail_min)
+                        / pcm->noirq_frames_per_msec;
+
+            err = pcm_wait(pcm, time);
+            if (err < 0) {
+                pcm->running = 0;
+                fprintf(stderr, "wait error: hw 0x%x app 0x%x avail 0x%x\n",
+                    (unsigned int)pcm->mmap_status->hw_ptr,
+                    (unsigned int)pcm->mmap_control->appl_ptr,
+                    avail);
+                pcm->mmap_control->appl_ptr = 0;
+                return err;
+            }
+            continue;
+        }
+
+        frames = count;
+        if (frames > avail)
+            frames = avail;
+
+        if (!frames)
+            break;
+
+        /* copy frames from buffer */
+        frames = pcm_mmap_write_areas(pcm, buffer, offset, frames);
+        if (frames < 0) {
+            fprintf(stderr, "write error: hw 0x%x app 0x%x avail 0x%x\n",
+                    (unsigned int)pcm->mmap_status->hw_ptr,
+                    (unsigned int)pcm->mmap_control->appl_ptr,
+                    avail);
+            return frames;
+        }
+
+        offset += frames;
+        count -= frames;
+    }
+
+    return 0;
+}
diff --git a/audio-tool/pulse-generator.c b/audio-tool/pulse-generator.c
new file mode 100644
index 0000000..bd7deff
--- /dev/null
+++ b/audio-tool/pulse-generator.c
@@ -0,0 +1,273 @@
+/* pulse-generator.c
+**
+** Copyright 2011, The Android Open Source Project
+**
+** 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.
+**     * Neither the name of The Android Open Source Project nor the names of
+**       its contributors may be used to endorse or promote products derived
+**       from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``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 Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#include <tinyalsa/asoundlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <time.h>
+#include <signal.h>
+#include <string.h>
+
+#include "config.h"
+#include "pulse-generator.h"
+
+static volatile int running = 1;
+
+static void sigint_handler(int sig)
+{
+    running = 0;
+}
+
+struct interval_tracker {
+    struct timespec last;
+    struct timespec now;
+    size_t count;
+    double sum_ms;
+    double sum2_ms;
+    double max_ms;
+    double min_ms;
+    int initd;
+};
+
+static void ivt_init(struct interval_tracker *it)
+{
+    memset(it, 0, sizeof(struct interval_tracker));
+}
+
+static void ivt_lap(struct interval_tracker *it)
+{
+    double elapsed;
+    clock_gettime(CLOCK_MONOTONIC, &it->now);
+    if (it->initd) {
+        it->count++;
+        elapsed = (it->now.tv_sec - it->last.tv_sec) * 1000000.0
+            + (it->now.tv_nsec - it->last.tv_nsec) / 1000.0;
+        it->sum_ms += elapsed;
+        it->sum2_ms += elapsed*elapsed;
+        if (it->count == 1) {
+            it->max_ms = it->min_ms = elapsed;
+        } else {
+            if (elapsed > it->max_ms)
+                it->max_ms = elapsed;
+            if (elapsed < it->min_ms)
+                it->min_ms = elapsed;
+        }
+#if 0
+        printf("now=%ld dt=%08u max=%08u min=%08u\n",
+               1000000 * (it->now.tv_sec % 100) + it->now.tv_nsec / 1000,
+               (unsigned)(elapsed+.5), (unsigned)(it->max_ms+.5), (unsigned)(it->min_ms+.5));
+#endif
+    }
+    it->last = it->now;
+    it->initd = 1;
+}
+
+static double ivt_average(struct interval_tracker *it)
+{
+    if (it->count <= 0)
+        return 0.0;
+    return it->sum_ms / (double)(it->count);
+}
+               
+#define PULSE_AT_FRONT  (1<<0)
+#define PULSE_AT_END    (1<<1)
+#define PULSE_AT_MIDDLE (1<<2)
+
+struct play_pulses_params {
+    unsigned int card;
+    unsigned int device;
+    unsigned int channels;
+    unsigned int rate;
+    unsigned int bits;
+    unsigned int period_size;
+    unsigned int period_count;
+    unsigned int pulse_position;
+};
+
+static void play_pulses(struct play_pulses_params *params);
+
+int pulse_generator_main(const struct audio_tool_config *config, int argc, char **argv)
+{
+    struct play_pulses_params params = {
+        .card = 0,
+        .device = 0,
+        .channels = 2,
+        .rate = 48000,
+        .bits = 16,
+        .period_size = 1024,
+        .period_count = 4,
+        .pulse_position = PULSE_AT_FRONT,
+    };
+
+    if (argc > 4) {
+	    printf("Usage: audio-tool [options] pulse [front] [middle] [end]\n");
+	    return 1;
+    } else if (argc > 1) {
+	    int n;
+	    params.pulse_position = 0;
+	    for (n = 1 ; n < argc ; ++n) {
+		    if (strcmp("front", argv[n]) == 0) {
+			    params.pulse_position |= PULSE_AT_FRONT;
+		    } else if (strcmp("middle", argv[n]) == 0) {
+			    params.pulse_position |= PULSE_AT_MIDDLE;
+		    } else if (strcmp("end", argv[n]) == 0) {
+			    params.pulse_position |= PULSE_AT_END;
+		    } else {
+			    printf("Usage: audio-tool [options] pulse [front] [middle] [end]\n");
+			    return 1;
+		    }
+	    }
+    }
+
+    params.device = config->device;
+    params.period_size = config->period_size;
+    params.period_count = config->num_periods;
+    params.card = config->card;
+    params.channels = config->channels;
+    params.rate = config->rate;
+    params.bits = config->bits;
+
+    signal(SIGINT, sigint_handler);
+    play_pulses(&params);
+
+    return 0;
+}
+
+static void play_pulses(struct play_pulses_params *params)
+{
+    struct pcm_config config;
+    struct pcm *pcm;
+    struct interval_tracker it;
+    char *buffer;
+    int size;
+    int num_read;
+
+    ivt_init(&it);
+
+    config.channels = params->channels;
+    config.rate = params->rate;
+    config.period_size = params->period_size;
+    config.period_count = params->period_count;
+    if (params->bits == 32)
+        config.format = PCM_FORMAT_S32_LE;
+    else if (params->bits == 16)
+        config.format = PCM_FORMAT_S16_LE;
+    config.start_threshold = params->period_size;
+    config.stop_threshold = params->period_size * params->period_count;
+    config.silence_threshold = 0;
+
+    pcm = pcm_open(params->card, params->device, PCM_OUT, &config);
+    if (!pcm || !pcm_is_ready(pcm)) {
+        fprintf(stderr, "Unable to open PCM device %u (%s)\n",
+                params->device, pcm_get_error(pcm));
+        return;
+    }
+
+    size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
+    printf("size = %d\n", size);
+    buffer = malloc(size);
+    if (!buffer) {
+        fprintf(stderr, "Unable to allocate %d bytes\n", size);
+        free(buffer);
+        pcm_close(pcm);
+        return;
+    }
+
+    printf("Playing sample: %u ch, %u hz, %u bit\n",
+           params->channels, params->rate, params->bits);
+
+    int k, stat;
+    int interrupt_tol = 20 + 1000000 * params->period_size / params->rate;
+    int pulse_position = params->pulse_position;
+    int period_bytes = pcm_frames_to_bytes(pcm, params->period_size);
+    char pulse_byte = 0x1f;
+    pcm_start(pcm);
+    do {
+        memset(buffer, 0, size);
+        num_read = period_bytes;
+        if (pulse_position & PULSE_AT_FRONT) {
+            for( k = 0 ; (k < size) && (k < 4) ; ++k ) {
+                buffer[k] = pulse_byte;
+            }
+        }
+        if (pulse_position & PULSE_AT_MIDDLE) {
+            for( k = (period_bytes/2) - 4 ; (k < size) && (k < (period_bytes/2)) ; ++k ) {
+                buffer[k] = pulse_byte;
+            }
+        }
+        if (pulse_position & PULSE_AT_END) {
+            for( k = period_bytes-1 ; (k > 0) && (k >= (period_bytes-4)) ; --k ) {
+                buffer[k] = pulse_byte;
+            }
+        }
+
+	stat = pcm_wait(pcm, interrupt_tol /* ms timeout */);
+	switch (stat) {
+	case 0: /* timeout */
+            printf("timeout");
+            break;
+	case 1:
+            ivt_lap(&it);
+            if (pcm_write(pcm, buffer, num_read)) {
+                fprintf(stderr, "Error playing sample\n");
+                num_read = 0;
+            }
+            break;
+	case -EINTR:
+            printf("wait was interrupted\n");
+            break;
+	case -EPIPE:
+            printf("xrun\n");
+            break;
+	case -ESTRPIPE:
+            printf("state suspended\n");
+            break;
+	case -ENODEV:
+            printf("disconnected\n");
+            num_read = 0;
+            break;
+	case -EIO:
+            printf("EIEIO\n");
+            num_read = 0;
+            break;
+	default:
+            printf("unknown error\n");
+	}
+
+    } while (running && (num_read > 0));
+
+    pcm_stop(pcm);
+
+    printf("avg=%g max=%g min=%g\n",
+           ivt_average(&it), it.max_ms, it.min_ms);
+
+    free(buffer);
+    pcm_close(pcm);
+}
diff --git a/audio-tool/pulse-generator.h b/audio-tool/pulse-generator.h
new file mode 100644
index 0000000..3487a5e
--- /dev/null
+++ b/audio-tool/pulse-generator.h
@@ -0,0 +1,33 @@
+/* pulse-generator.h
+**
+** Copyright 2011, The Android Open Source Project
+**
+** 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.
+**     * Neither the name of The Android Open Source Project nor the names of
+**       its contributors may be used to endorse or promote products derived
+**       from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``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 Android Open Source Project 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.
+*/
+#ifndef __AUDIO_TOOL_PULSE_GENERATOR_H__
+#define __AUDIO_TOOL_PULSE_GENERATOR_H__
+
+int pulse_generator_main(const struct audio_tool_config *config, int argc, char **argv);
+
+#endif /* __AUDIO_TOOL_PULSE_GENERATOR_H__ */
diff --git a/audio-tool/restore.c b/audio-tool/restore.c
new file mode 100644
index 0000000..6de1927
--- /dev/null
+++ b/audio-tool/restore.c
@@ -0,0 +1,217 @@
+/*
+ * restore.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <tinyalsa/asoundlib.h>
+
+#include "config.h"
+#include "restore.h"
+#include "mixer_cache.h"
+
+#define BUFSIZE (1L << 15)
+
+static int read_line(char *dest, size_t max, const char *src, size_t size, size_t *offset)
+{
+	size_t n;
+
+	for (n = 0 ; (n < max) && (src[*offset] != '\n') && (*offset < size) ; ++n, *offset += 1) {
+		dest[n] = src[*offset];
+	}
+
+	if (n < max)
+		dest[n] = '\0';
+	else
+		dest[n-1] = '\0';
+
+	if ((*offset < size) && (src[*offset] == '\n'))
+		*offset += 1;
+
+	return n;
+}
+
+/* Notes:
+ *   - param 'line' is modified by this function
+ *   - param 'db' has mixer_cache_touch() called on it.
+ */
+static void process_line(char* line, struct audio_tool_mixer_cache *db, struct mixer *mixer)
+{
+	size_t length = strlen(line);
+	size_t pos = 0, sep;
+	const char* sep_ptr;
+	enum field_t { FNAME=0, FTYPE, FCOUNT, FVALS } field = FNAME;
+	unsigned control_id, val, val_no;
+	const char *ctl_type;
+	unsigned file_count, ctl_count;
+	struct mixer_ctl *ctl;
+
+	if (!length)
+		return;
+
+	if (line[0] == '#')
+		return;
+
+	control_id = -1;
+	val_no = 0;
+	while (pos < length) {
+		sep_ptr = strstr(&line[pos], "\t");
+		if (sep_ptr)
+			sep = (size_t) (sep_ptr - line);
+		else
+			sep = length;
+		if (sep < length)
+			line[sep] = '\0';
+		switch(field) {
+		case FNAME:
+			control_id = mixer_cache_get_id_by_name(db, &line[pos]);
+			if (control_id == -1) {
+				printf("Error: could not find control %s\n", &line[pos]);
+				pos = length;
+			}
+			ctl = mixer_get_ctl(mixer, control_id);
+			break;
+		case FTYPE:
+			ctl_type = mixer_ctl_get_type_string(ctl);
+			if (0 != strcmp(ctl_type, &line[pos])) {
+				printf("Error: type mismatch for control #%d: file=%s card=%s\n",
+				       control_id, &line[pos], ctl_type);
+				pos = length;
+			}
+			break;
+		case FCOUNT:
+			ctl_count = mixer_ctl_get_num_values(ctl);
+			file_count = atoi(&line[pos]);
+			if (ctl_count != file_count) {
+				printf("Error: mismatch in the count of control #%d's values: "
+				       "file=%d card=%d\n", control_id, file_count, ctl_count);
+				pos = length;
+			}
+			break;
+		case FVALS:
+			if (0 == strcmp("#N/A", &line[pos])) {
+				pos = length;
+			} else if (0 == strcmp("ENUM", ctl_type)) {
+				mixer_ctl_set_enum_by_string(ctl, &line[pos]);
+			} else {
+				val = atoi(&line[pos]);
+				mixer_ctl_set_value(ctl, val_no, val);
+			}
+			++val_no;
+			break;
+		}
+
+		if (field != FVALS)
+			++field;
+
+		pos = sep + 1;
+	}
+	mixer_cache_touch(db, control_id);
+}
+
+int restore_main(const struct audio_tool_config *config, int argc, char **argv)
+{
+	struct mixer *mixer;
+	int card = config->card;
+	int ret = 0;
+	char *filename = 0;
+	char buf[BUFSIZE];
+	int fd;
+	char *src;
+	size_t cursor;
+	struct stat fd_stat;
+	struct audio_tool_mixer_cache db;
+
+	if (argc != 2) {
+		printf("Usage: audio-tool restore <filename>\n");
+		return 1;
+	}
+
+	filename = argv[1];
+
+	mixer = mixer_open(card);
+	if (!mixer) {
+		printf("Could not open mixer for card %d\n", card);
+		return 1;
+	}
+
+	fd = open(filename, O_RDONLY);
+	if (fd == -1) {
+		printf("Could not open file %s for reading (error=%s)\n", filename, strerror(errno));
+		return 1;
+	}
+
+	if(-1 == fstat(fd, &fd_stat)) {
+		printf("Could not stat file %s\n", filename);
+		return 1;
+	}
+
+	src = mmap(NULL, fd_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	if (src == MAP_FAILED) {
+		printf("Could not mmap the file %s (error=%s)\n", filename, strerror(errno));
+		return 1;
+	}
+
+	if (mixer_cache_init(&db)) {
+		printf("Could not initialize mixer cache\n");
+		return 1;
+	}
+	if (mixer_cache_populate(&db, mixer)) {
+		printf("Could not populate mixer cache\n");
+		return 1;
+	}
+
+	mixer_cache_reset_touch(&db);
+	cursor = 0;
+	while(read_line(buf, BUFSIZE, src, fd_stat.st_size, &cursor)) {
+		process_line(buf, &db, mixer);
+	}
+	ret = mixer_cache_audit_touch(&db, 1);
+
+	munmap(src, fd_stat.st_size);
+	close(fd);
+	mixer_close(mixer);
+
+	return ret;
+}
+
diff --git a/audio-tool/restore.h b/audio-tool/restore.h
new file mode 100644
index 0000000..8d9f366
--- /dev/null
+++ b/audio-tool/restore.h
@@ -0,0 +1,43 @@
+/*
+ * restore.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __AUDIO_TOOL_RESTORE_H__
+#define __AUDIO_TOOL_RESTORE_H__
+
+int restore_main(const struct audio_tool_config *config, int argc, char **argv);
+
+#endif /* __OMAP_AUDIO_TOOL_RESTORE_H__ */
+
diff --git a/audio-tool/rms.c b/audio-tool/rms.c
new file mode 100644
index 0000000..52212cb
--- /dev/null
+++ b/audio-tool/rms.c
@@ -0,0 +1,121 @@
+/*
+ * rms.c
+ *
+ * calculate RMS of a wav file per channel
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <math.h>
+#include <inttypes.h>
+
+#include "rms.h"
+
+#define MAX_FILENAME_LEN 256
+#define MAX_CHANNELS 8
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT  0x20746d66
+#define ID_DATA 0x61746164
+
+struct wav_header {
+	uint32_t riff_id;
+	uint32_t riff_sz;
+	uint32_t riff_fmt;
+	uint32_t fmt_id;
+	uint32_t fmt_sz;
+	uint16_t audio_format;
+	uint16_t num_channels;
+	uint32_t sample_rate;
+	uint32_t byte_rate;
+	uint16_t block_align;
+	uint16_t bits_per_sample;
+	uint32_t data_id;
+	uint32_t data_sz;
+};
+
+/* Reuse tinyplay/tinycap code for header parsing */
+static int parse_header (char * filename, struct wav_header * wav_header) {
+	FILE* file;
+	file = fopen(filename, "rb");
+	if (!file) {
+		fprintf(stderr, "Unable to open file '%s'\n", filename);
+		return 1;
+	}
+	fread(wav_header, sizeof(struct wav_header), 1, file);
+	if ((wav_header->riff_id != ID_RIFF) ||
+			(wav_header->riff_fmt != ID_WAVE) ||
+			(wav_header->data_id != ID_DATA)) {
+		fprintf(stderr, "Error: '%s' is not a PCM Wav file\n", filename);
+		fclose(file);
+		return 1;
+	}
+	fclose(file);
+	return 0;
+}
+
+static int rms (char *filename, const struct wav_header * wav_header) {
+	char outname[MAX_FILENAME_LEN];
+	uint8_t i = 0;
+	char *extension_pos;
+	FILE *fp;                   /* input file pointer  */
+	uint16_t data[MAX_CHANNELS];
+	uint64_t rms[MAX_CHANNELS];
+	uint64_t num_samples = 0;
+
+	fp = fopen(filename, "rb");
+	/* Jump to data block  */
+	fseek(fp, sizeof(struct wav_header), SEEK_SET);
+
+	strncpy(outname, filename, sizeof(outname));
+	/* Get extension index of filename */
+	extension_pos = strrchr(outname, '.');
+	/* Point to '\0' instead if no extension is found */
+	if (!extension_pos)
+		extension_pos = strrchr(outname, '\0');
+	if (!extension_pos) {
+		fprintf(stderr, "Filename is too long!\n");
+		return 1;
+	}
+
+	/* Split data block */
+	while(fread(data, wav_header->block_align, 1, fp) == 1) {
+		num_samples++;
+		for (i = 0; i < wav_header->num_channels; i++)
+                        rms[i] += data[i]*data[i];
+	}
+
+	for (i = 0; i < wav_header->num_channels; i++) {
+		printf("channel %d rms is %f\n",i, sqrt((float)rms[i])/num_samples);
+	}
+	fclose(fp);
+	return 0;
+}
+
+int rms_main (int argc, char* argv[]) {
+	char *filename;
+	struct wav_header wav_header, wav_header_out;
+
+	if (argc != 2) {
+		fprintf(stderr, "Usage: audio-tool rms file.wav\n");
+		return 1;
+	}
+	filename = argv[1];
+
+	if (parse_header(filename, &wav_header))
+		return 1;
+
+	if (wav_header.num_channels > MAX_CHANNELS) {
+		fprintf(stderr, "Error: only support %d or less channels\n", MAX_CHANNELS);
+		return 1;
+	}
+
+	if(rms(filename, &wav_header))
+		return 1;
+
+	return 0;
+}
+
diff --git a/audio-tool/rms.h b/audio-tool/rms.h
new file mode 100644
index 0000000..f053064
--- /dev/null
+++ b/audio-tool/rms.h
@@ -0,0 +1,7 @@
+#ifndef __AUDIO_TOOL_RMS_H__
+#define __AUDIO_TOOL_RMS_H__
+
+int rms_main (int argc, char* argv[]);
+
+#endif /* __AUDIO_TOOL_RMS_H__ */
+
diff --git a/audio-tool/save.c b/audio-tool/save.c
new file mode 100644
index 0000000..fc345b8
--- /dev/null
+++ b/audio-tool/save.c
@@ -0,0 +1,109 @@
+/*
+ * save.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <tinyalsa/asoundlib.h>
+
+#include "config.h"
+#include "save.h"
+
+int save_main(const struct audio_tool_config *config, int argc, char **argv)
+{
+	struct mixer *mixer;
+	int card = config->card;
+	char *filename = 0;
+	FILE *dest;
+	unsigned count, n;
+
+	if (argc != 2) {
+		printf("Usage: audio-tool save <filename>\n");
+		return 1;
+	}
+
+	filename = argv[1];
+
+	mixer = mixer_open(card);
+	if (!mixer) {
+		printf("Could not open mixer for card %d\n", card);
+		return 1;
+	}
+
+	dest = fopen(filename, "wt");
+	if (!dest) {
+		printf("Could not open file %s for writing\n", filename);
+		return 1;
+	}
+
+	fprintf(dest, "# Format: CTL_NAME<tab>CTL_TYPE<tab>NUM_VALS<tab>VAL1"
+		"<tab>VAL2...\n");
+	count = mixer_get_num_ctls(mixer);
+	for (n = 0 ; n < count ; ++n) {
+		struct mixer_ctl *ctl;
+		const char *ctl_name, *ctl_type;
+		enum mixer_ctl_type type;
+		unsigned val, num_vals, k;
+
+		ctl = mixer_get_ctl(mixer, n);
+		ctl_name = mixer_ctl_get_name(ctl);
+		ctl_type = mixer_ctl_get_type_string(ctl);
+		num_vals = mixer_ctl_get_num_values(ctl);
+		type = mixer_ctl_get_type(ctl);
+		fprintf(dest, "%s\t%s\t%u", ctl_name, ctl_type, num_vals);
+
+		for (k = 0 ; k < num_vals ; ++k) {
+			switch(type) {
+			case MIXER_CTL_TYPE_INT:
+			case MIXER_CTL_TYPE_BOOL:
+			case MIXER_CTL_TYPE_BYTE:
+				fprintf(dest, "\t%u", mixer_ctl_get_value(ctl, k));
+				break;
+			case MIXER_CTL_TYPE_ENUM:
+				val = mixer_ctl_get_value(ctl, k);
+				fprintf(dest, "\t%s", mixer_ctl_get_enum_string(ctl, val));
+				break;
+			default:
+				fprintf(dest, "\t#N/A");
+			}
+		}
+		fprintf(dest, "\n");
+	}
+
+	fclose(dest);
+	mixer_close(mixer);
+
+	return 0;
+}
+
diff --git a/audio-tool/save.h b/audio-tool/save.h
new file mode 100644
index 0000000..3d30d91
--- /dev/null
+++ b/audio-tool/save.h
@@ -0,0 +1,43 @@
+/*
+ * save.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ *  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.
+ *
+ *    Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "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 COPYRIGHT
+ *  OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __AUDIO_TOOL_SAVE_H__
+#define __AUDIO_TOOL_SAVE_H__
+
+int save_main(const struct audio_tool_config *config, int argc, char **argv);
+
+#endif /* __OMAP_AUDIO_TOOL_SAVE_H__ */
+
diff --git a/audio-tool/tinycap.c b/audio-tool/tinycap.c
new file mode 100644
index 0000000..6b87dc4
--- /dev/null
+++ b/audio-tool/tinycap.c
@@ -0,0 +1,216 @@
+/* tinycap.c
+**
+** Copyright 2011, The Android Open Source Project
+**
+** 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.
+**     * Neither the name of The Android Open Source Project nor the names of
+**       its contributors may be used to endorse or promote products derived
+**       from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``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 Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#include <tinyalsa/asoundlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <signal.h>
+#include <string.h>
+
+#include "config.h"
+#include "tinycap.h"
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT  0x20746d66
+#define ID_DATA 0x61746164
+
+#define FORMAT_PCM 1
+
+struct wav_header {
+    uint32_t riff_id;
+    uint32_t riff_sz;
+    uint32_t riff_fmt;
+    uint32_t fmt_id;
+    uint32_t fmt_sz;
+    uint16_t audio_format;
+    uint16_t num_channels;
+    uint32_t sample_rate;
+    uint32_t byte_rate;
+    uint16_t block_align;
+    uint16_t bits_per_sample;
+    uint32_t data_id;
+    uint32_t data_sz;
+};
+
+int capturing = 1;
+
+static
+unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
+                            unsigned int channels, unsigned int rate,
+                            unsigned int bits, unsigned int period_size,
+                            unsigned int period_count, unsigned int duration);
+
+void sigint_handler(int sig)
+{
+    capturing = 0;
+}
+
+int tinycap_main(const struct audio_tool_config *config, int argc, char **argv,
+		int legacy_mode)
+{
+    FILE *file;
+    struct wav_header header;
+    unsigned int card = 0;
+    unsigned int device = 0;
+    unsigned int channels = 2;
+    unsigned int rate = 44100;
+    unsigned int bits = 16;
+    unsigned int frames;
+    unsigned int period_size = 1024;
+    unsigned int period_count = 4;
+    unsigned int duration = 0;
+    int arg;
+
+    if ((!legacy_mode && argc != 2) || (legacy_mode && argc != 1)) {
+        fprintf(stderr, "Usage: audio-tool [options] capture file.wav\n");
+        return 1;
+    }
+
+    if (legacy_mode)
+	    arg = 0;
+    else
+	    arg = 1;
+
+    file = fopen(argv[arg], "wb");
+    if (!file) {
+        fprintf(stderr, "Unable to create file '%s'\n", argv[arg]);
+        return 1;
+    }
+
+    device = config->device;
+    channels = config->channels;
+    rate = config->rate;
+    bits = config->bits;
+    card = config->card;
+    period_size = config->period_size;
+    period_count = config->num_periods;
+    duration = config->duration;
+
+    header.riff_id = ID_RIFF;
+    header.riff_sz = 0;
+    header.riff_fmt = ID_WAVE;
+    header.fmt_id = ID_FMT;
+    header.fmt_sz = 16;
+    header.audio_format = FORMAT_PCM;
+    header.num_channels = channels;
+    header.sample_rate = rate;
+    header.bits_per_sample = bits;
+    header.byte_rate = (header.bits_per_sample / 8) * channels * rate;
+    header.block_align = channels * (header.bits_per_sample / 8);
+    header.data_id = ID_DATA;
+
+    /* leave enough room for header */
+    fseek(file, sizeof(struct wav_header), SEEK_SET);
+
+    /* install signal handler and begin capturing */
+    signal(SIGINT, sigint_handler);
+    frames = capture_sample(file, card, device, header.num_channels,
+                            header.sample_rate, header.bits_per_sample,
+                            period_size, period_count, duration);
+    printf("Captured %d frames\n", frames);
+
+    /* write header now all information is known */
+    header.data_sz = frames * header.block_align;
+    fseek(file, 0, SEEK_SET);
+    fwrite(&header, sizeof(struct wav_header), 1, file);
+
+    fclose(file);
+
+    return 0;
+}
+
+static
+unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
+                            unsigned int channels, unsigned int rate,
+                            unsigned int bits, unsigned int period_size,
+                            unsigned int period_count, unsigned int duration)
+{
+    struct pcm_config config;
+    struct pcm *pcm;
+    char *buffer;
+    unsigned int size;
+    unsigned int bytes_read = 0;
+    unsigned long requested;
+
+    config.channels = channels;
+    config.rate = rate;
+    config.period_size = period_size;
+    config.period_count = period_count;
+    if (bits == 32) {
+        config.format = PCM_FORMAT_S32_LE;
+    } else if (bits == 24) {
+        config.format = PCM_FORMAT_S24_3LE;
+    } else if (bits == 16) {
+        config.format = PCM_FORMAT_S16_LE;
+    } else {
+      fprintf(stderr, "Bits per sample %d not supported\n", bits);
+      return 0;
+    }
+    config.start_threshold = 0;
+    config.stop_threshold = 0;
+    config.silence_threshold = 0;
+
+    pcm = pcm_open(card, device, PCM_IN, &config);
+    if (!pcm || !pcm_is_ready(pcm)) {
+        fprintf(stderr, "Unable to open PCM device (%s)\n",
+                pcm_get_error(pcm));
+        return 0;
+    }
+
+    size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
+    buffer = malloc(size);
+    if (!buffer) {
+        fprintf(stderr, "Unable to allocate %d bytes\n", size);
+        free(buffer);
+        pcm_close(pcm);
+        return 0;
+    }
+
+    if (duration)
+        requested = pcm_frames_to_bytes(pcm, rate * duration);
+    else
+        requested = 0;
+
+    printf("Capturing sample: %u ch, %u hz, %u bit\n", channels, rate, bits);
+
+    while (capturing && (!requested || (bytes_read < requested)) &&
+           !pcm_read(pcm, buffer, size)) {
+        if (fwrite(buffer, 1, size, file) != size) {
+            fprintf(stderr,"Error capturing sample\n");
+            break;
+        }
+        bytes_read += size;
+    }
+
+    free(buffer);
+    pcm_close(pcm);
+
+    return bytes_read / ((bits / 8) * channels);
+}
diff --git a/audio-tool/tinycap.h b/audio-tool/tinycap.h
new file mode 100644
index 0000000..b36f14a
--- /dev/null
+++ b/audio-tool/tinycap.h
@@ -0,0 +1,34 @@
+/* tinycap.h
+**
+** Copyright 2011, The Android Open Source Project
+**
+** 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.
+**     * Neither the name of The Android Open Source Project nor the names of
+**       its contributors may be used to endorse or promote products derived
+**       from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``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 Android Open Source Project 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.
+*/
+#ifndef __TINYALSA_TINYCAP_H__
+#define __TINYALSA_TINYCAP_H__
+
+int tinycap_main(const struct audio_tool_config *config, int argc, char **argv,
+		int legacy_mode);
+
+#endif /* __TINYALSA_TINYCAP_H__ */
diff --git a/audio-tool/tinymix.c b/audio-tool/tinymix.c
new file mode 100644
index 0000000..babce63
--- /dev/null
+++ b/audio-tool/tinymix.c
@@ -0,0 +1,255 @@
+/* tinymix.c
+**
+** Copyright 2011, The Android Open Source Project
+**
+** 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.
+**     * Neither the name of The Android Open Source Project nor the names of
+**       its contributors may be used to endorse or promote products derived
+**       from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``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 Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#include <assert.h>
+#include <tinyalsa/asoundlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "config.h"
+#include "tinymix.h"
+
+static void tinymix_list_controls(struct mixer *mixer);
+static void tinymix_detail_control(struct mixer *mixer, unsigned int id,
+                                   int print_all);
+static void tinymix_set_value(struct mixer *mixer, unsigned int id,
+                              char *value);
+static void tinymix_print_enum(struct mixer_ctl *ctl, int print_all);
+static int tinymix_string_is_integer(const char *str);
+static unsigned int tinymix_get_control_id_for_name(struct mixer *mixer, const char *str);
+
+int tinymix_main(const struct audio_tool_config *config, int argc, char **argv,
+		int legacy_mode)
+{
+    struct mixer *mixer;
+    int card = 0;
+    unsigned int ctrl_id;
+
+    card = config->card;
+
+    mixer = mixer_open(card);
+    if (!mixer) {
+        fprintf(stderr, "Failed to open mixer\n");
+        return EXIT_FAILURE;
+    }
+
+    if (!legacy_mode) {
+	    --argc;
+	    ++argv;
+    }
+
+    if (argc > 0)
+        ctrl_id = tinymix_get_control_id_for_name(mixer, argv[0]);
+
+    if (argc == 0)
+        tinymix_list_controls(mixer);
+    else if (argc == 1)
+        tinymix_detail_control(mixer, ctrl_id, 1);
+    else if (argc == 2)
+        tinymix_set_value(mixer, ctrl_id, argv[1]);
+    else
+        printf("Usage: tinymix [-D card] [control id] [value to set]\n");
+
+    mixer_close(mixer);
+
+    return 0;
+}
+
+static int tinymix_string_is_integer(const char *str)
+{
+    const char *p;
+
+    assert(str);
+
+    for (p = str ; p != '\0' ; ++p) {
+        if (!isdigit(*p)) {
+            return 0;
+        }
+    }
+
+    if (p == str) {
+        return 0;
+    }
+
+    return 1;
+}
+
+static unsigned int tinymix_get_control_id_for_name(struct mixer *mixer, const char *str)
+{
+    if (tinymix_string_is_integer(str)) {
+        int v = atoi(str);
+        if (v < 0) {
+            v = 0;
+        }
+        return (unsigned int)v;
+    } else {
+        unsigned int num_ctls;
+        unsigned int n;
+        struct mixer_ctl *ctl;
+        const char *name;
+
+        num_ctls = mixer_get_num_ctls(mixer);
+        for (n = 0 ; n < num_ctls ; ++n) {
+            ctl = mixer_get_ctl(mixer, n);
+            name = mixer_ctl_get_name(ctl);
+            if (0 == strcmp(name, str)) {
+                return n;
+            }
+        }
+    }
+
+    return 0;
+}
+
+static void tinymix_list_controls(struct mixer *mixer)
+{
+    struct mixer_ctl *ctl;
+    const char *name, *type;
+    unsigned int num_ctls, num_values;
+    unsigned int i;
+
+    num_ctls = mixer_get_num_ctls(mixer);
+
+    printf("Number of controls: %d\n", num_ctls);
+
+    printf("ctl\ttype\tnum\t%-40s value\n", "name");
+    for (i = 0; i < num_ctls; i++) {
+        ctl = mixer_get_ctl(mixer, i);
+
+        name = mixer_ctl_get_name(ctl);
+        type = mixer_ctl_get_type_string(ctl);
+        num_values = mixer_ctl_get_num_values(ctl);
+        printf("%d\t%s\t%d\t%-40s", i, type, num_values, name);
+        tinymix_detail_control(mixer, i, 0);
+    }
+}
+
+static void tinymix_print_enum(struct mixer_ctl *ctl, int print_all)
+{
+    unsigned int num_enums;
+    unsigned int i;
+    const char *string;
+
+    num_enums = mixer_ctl_get_num_enums(ctl);
+
+    for (i = 0; i < num_enums; i++) {
+        string = mixer_ctl_get_enum_string(ctl, i);
+        if (print_all)
+            printf("\t%s%s", mixer_ctl_get_value(ctl, 0) == (int)i ? ">" : "",
+                   string);
+        else if (mixer_ctl_get_value(ctl, 0) == (int)i)
+            printf(" %-s", string);
+    }
+}
+
+static void tinymix_detail_control(struct mixer *mixer, unsigned int id,
+                                   int print_all)
+{
+    struct mixer_ctl *ctl;
+    enum mixer_ctl_type type;
+    unsigned int num_values;
+    unsigned int i;
+    int min, max;
+
+    if (id >= mixer_get_num_ctls(mixer)) {
+        fprintf(stderr, "Invalid mixer control\n");
+        return;
+    }
+
+    ctl = mixer_get_ctl(mixer, id);
+
+    type = mixer_ctl_get_type(ctl);
+    num_values = mixer_ctl_get_num_values(ctl);
+
+    if (print_all)
+        printf("%s:", mixer_ctl_get_name(ctl));
+
+    for (i = 0; i < num_values; i++) {
+        switch (type)
+        {
+        case MIXER_CTL_TYPE_INT:
+            printf(" %d", mixer_ctl_get_value(ctl, i));
+            break;
+        case MIXER_CTL_TYPE_BOOL:
+            printf(" %s", mixer_ctl_get_value(ctl, i) ? "On" : "Off");
+            break;
+        case MIXER_CTL_TYPE_ENUM:
+            tinymix_print_enum(ctl, print_all);
+            break;
+         case MIXER_CTL_TYPE_BYTE:
+            printf(" 0x%02x", mixer_ctl_get_value(ctl, i));
+            break;
+        default:
+            printf(" unknown");
+            break;
+        };
+    }
+
+    if (print_all) {
+        if (type == MIXER_CTL_TYPE_INT) {
+            min = mixer_ctl_get_range_min(ctl);
+            max = mixer_ctl_get_range_max(ctl);
+            printf(" (range %d->%d)", min, max);
+        }
+    }
+    printf("\n");
+}
+
+static void tinymix_set_value(struct mixer *mixer, unsigned int id,
+                              char *string)
+{
+    struct mixer_ctl *ctl;
+    enum mixer_ctl_type type;
+    unsigned int num_values;
+    unsigned int i;
+
+    ctl = mixer_get_ctl(mixer, id);
+    type = mixer_ctl_get_type(ctl);
+    num_values = mixer_ctl_get_num_values(ctl);
+
+    if (isdigit(string[0])) {
+        int value = atoi(string);
+
+        for (i = 0; i < num_values; i++) {
+            if (mixer_ctl_set_value(ctl, i, value)) {
+                fprintf(stderr, "Error: invalid value\n");
+                return;
+            }
+        }
+    } else {
+        if (type == MIXER_CTL_TYPE_ENUM) {
+            if (mixer_ctl_set_enum_by_string(ctl, string))
+                fprintf(stderr, "Error: invalid enum value\n");
+        } else {
+            fprintf(stderr, "Error: only enum types can be set with strings\n");
+        }
+    }
+}
+
diff --git a/audio-tool/tinymix.h b/audio-tool/tinymix.h
new file mode 100644
index 0000000..be27666
--- /dev/null
+++ b/audio-tool/tinymix.h
@@ -0,0 +1,34 @@
+/* tinycap.h
+**
+** Copyright 2011, The Android Open Source Project
+**
+** 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.
+**     * Neither the name of The Android Open Source Project nor the names of
+**       its contributors may be used to endorse or promote products derived
+**       from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``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 Android Open Source Project 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.
+*/
+#ifndef __TINYALSA_TINYMIX_H__
+#define __TINYALSA_TINYMIX_H__
+
+int tinymix_main(const struct audio_tool_config *config, int argc, char **argv,
+		int legacy_mode);
+
+#endif /* __TINYALSA_TINYMIX_H__ */
diff --git a/audio-tool/tinyplay.c b/audio-tool/tinyplay.c
new file mode 100644
index 0000000..507dac4
--- /dev/null
+++ b/audio-tool/tinyplay.c
@@ -0,0 +1,205 @@
+/* tinyplay.c
+**
+** Copyright 2011, The Android Open Source Project
+**
+** 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.
+**     * Neither the name of The Android Open Source Project nor the names of
+**       its contributors may be used to endorse or promote products derived
+**       from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``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 Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#include <tinyalsa/asoundlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "config.h"
+#include "tinyplay.h"
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT  0x20746d66
+#define ID_DATA 0x61746164
+
+struct riff_wave_header {
+    uint32_t riff_id;
+    uint32_t riff_sz;
+    uint32_t wave_id;
+};
+
+struct chunk_header {
+    uint32_t id;
+    uint32_t sz;
+};
+
+struct chunk_fmt {
+    uint16_t audio_format;
+    uint16_t num_channels;
+    uint32_t sample_rate;
+    uint32_t byte_rate;
+    uint16_t block_align;
+    uint16_t bits_per_sample;
+};
+
+static
+void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
+                 unsigned int rate, unsigned int bits, unsigned int period_size,
+                 unsigned int period_count, unsigned int duration);
+
+int tinyplay_main(const struct audio_tool_config *config, int argc, char **argv,
+		int legacy_mode)
+{
+    FILE *file;
+    struct riff_wave_header riff_wave_header;
+    struct chunk_header chunk_header;
+    struct chunk_fmt chunk_fmt;
+    unsigned int device = 0;
+    unsigned int card = 0;
+    unsigned int period_size = 1024;
+    unsigned int period_count = 4;
+    unsigned int duration = -1;
+    int arg;
+    char *filename;
+    int more_chunks = 1;
+
+    if ((!legacy_mode && argc != 2) || (legacy_mode && argc != 1)) {
+        fprintf(stderr, "Usage: audio-tool [options] play file.wav\n");
+        return 1;
+    }
+
+    if (legacy_mode)
+	    arg = 0;
+    else
+	    arg = 1;
+
+    filename = argv[1];
+    file = fopen(filename, "rb");
+    if (!file) {
+        fprintf(stderr, "Unable to open file '%s'\n", filename);
+        return 1;
+    }
+
+    device = config->device;
+    period_size = config->period_size;
+    period_count = config->num_periods;
+    card = config->card;
+    duration = config->duration;
+
+    fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
+    if ((riff_wave_header.riff_id != ID_RIFF) ||
+        (riff_wave_header.wave_id != ID_WAVE)) {
+        fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
+        fclose(file);
+        return 1;
+    }
+
+    do {
+        fread(&chunk_header, sizeof(chunk_header), 1, file);
+
+        switch (chunk_header.id) {
+        case ID_FMT:
+            fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
+            /* If the format header is larger, skip the rest */
+            if (chunk_header.sz > sizeof(chunk_fmt))
+                fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
+            break;
+        case ID_DATA:
+            /* Stop looking for chunks */
+            more_chunks = 0;
+            break;
+        default:
+            /* Unknown chunk, skip bytes */
+            fseek(file, chunk_header.sz, SEEK_CUR);
+        }
+    } while (more_chunks);
+
+    play_sample(file, card, device, chunk_fmt.num_channels, chunk_fmt.sample_rate,
+                chunk_fmt.bits_per_sample, period_size, period_count, duration);
+
+    fclose(file);
+
+    return 0;
+}
+
+static
+void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
+                 unsigned int rate, unsigned int bits, unsigned int period_size,
+                 unsigned int period_count, unsigned int duration)
+{
+    struct pcm_config config;
+    struct pcm *pcm;
+    char *buffer;
+    int size;
+    int num_read;
+    unsigned long requested;
+    unsigned long written = 0;
+
+    config.channels = channels;
+    config.rate = rate;
+    config.period_size = period_size;
+    config.period_count = period_count;
+    if (bits == 32)
+        config.format = PCM_FORMAT_S32_LE;
+    else if (bits == 16)
+        config.format = PCM_FORMAT_S16_LE;
+    config.start_threshold = 0;
+    config.stop_threshold = 0;
+    config.silence_threshold = 0;
+
+    pcm = pcm_open(card, device, PCM_OUT, &config);
+    if (!pcm || !pcm_is_ready(pcm)) {
+        fprintf(stderr, "Unable to open PCM device %u (%s)\n",
+                device, pcm_get_error(pcm));
+        return;
+    }
+
+    size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
+    buffer = malloc(size);
+    if (!buffer) {
+        fprintf(stderr, "Unable to allocate %d bytes\n", size);
+        free(buffer);
+        pcm_close(pcm);
+        return;
+    }
+
+    if (duration)
+        requested = pcm_frames_to_bytes(pcm, rate * duration);
+    else
+        requested = 0;
+
+    printf("Playing sample: %u ch, %u hz, %u bit\n", channels, rate, bits);
+
+    do {
+        num_read = fread(buffer, 1, size, file);
+        if (num_read > 0) {
+            if (pcm_write(pcm, buffer, num_read)) {
+                fprintf(stderr, "Error playing sample\n");
+                break;
+            }
+        }
+        written += num_read;
+    } while ((num_read > 0) && (!requested || (written < requested)));
+
+    free(buffer);
+    pcm_close(pcm);
+}
+
diff --git a/audio-tool/tinyplay.h b/audio-tool/tinyplay.h
new file mode 100644
index 0000000..89b4920
--- /dev/null
+++ b/audio-tool/tinyplay.h
@@ -0,0 +1,34 @@
+/* tinyplay.h
+**
+** Copyright 2011, The Android Open Source Project
+**
+** 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.
+**     * Neither the name of The Android Open Source Project nor the names of
+**       its contributors may be used to endorse or promote products derived
+**       from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``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 Android Open Source Project 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.
+*/
+#ifndef __TINYALSA_TINYPLAY_H__
+#define __TINYALSA_TINYPLAY_H__
+
+int tinyplay_main(const struct audio_tool_config *config, int argc, char **argv,
+		int legacy_mode);
+
+#endif /* __TINYALSA_TINYPLAY_H__ */
diff --git a/audio-tool/tone-generator.c b/audio-tool/tone-generator.c
new file mode 100644
index 0000000..713e2c5
--- /dev/null
+++ b/audio-tool/tone-generator.c
@@ -0,0 +1,274 @@
+/* Copyright (c) 2011, Gabriel M. Beddingfield <gabrbedd@gmail.com>
+ * 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.
+ *     * Neither the name of the <organization> nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 <COPYRIGHT HOLDER> 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.
+ */
+
+/*
+ * tone-generator.c
+ *
+ * Utility for generating an accurate waveform to an audio output.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <stdint.h>
+#include <limits.h>
+#include <tinyalsa/asoundlib.h>
+#include "oscillator-table.h"
+
+#include "config.h"
+#include "tone-generator.h"
+
+/* LOAD ALL THE WAVE TABLES
+ *
+ * WAVE TABLE ASSUMPTIONS:
+ *
+ *  - All lengths are a power of 2 (8, 16, 32, 64, ...)
+ *
+ *  - The wave tables are sufficiently large such that
+ *    interpolation is not necessary.  For example, at a
+ *    sample rate of 48000 Hz, a wave table length 4096
+ *    should be sufficient for all frequencies above
+ *    24 Hz (48000/4096 = 11.72 Hz).
+ */
+static int16_t g_table_square_wave_data[] = {
+#include "table_square.c"
+};
+
+static int16_t g_table_sine_wave_data[] = {
+#include "table_sine.c"
+};
+
+static int16_t g_table_triangle_wave_data[] = {
+#include "table_triangle.c"
+};
+
+static int16_t g_table_sawtooth_wave_data[] = {
+#include "table_sawtooth.c"
+};
+
+static struct wave_table g_wave_tables[] = {
+	DECLARE_TABLE("square", g_table_square_wave_data),
+	DECLARE_TABLE("sine", g_table_sine_wave_data),
+	DECLARE_TABLE("triangle", g_table_triangle_wave_data),
+	DECLARE_TABLE("sawtooth", g_table_sawtooth_wave_data),
+	{ 0 }
+};
+
+static int check_wave_tables()
+{
+	assert( STATIC_ARRAY_SIZE(g_table_square_wave_data) <= 0xFFFF );
+	assert( STATIC_ARRAY_SIZE(g_table_sine_wave_data) <= 0xFFFF );
+	assert( STATIC_ARRAY_SIZE(g_table_triangle_wave_data) <= 0xFFFF );
+	assert( STATIC_ARRAY_SIZE(g_table_sawtooth_wave_data) <= 0xFFFF );
+
+	return 0;
+}
+
+struct tone_generator_config {
+	int card;
+	int device;
+	struct wave_table *wave_table;
+	struct wave_scale wave_scale;
+	struct pcm_config pcm_config;
+	uint32_t duration;
+	int16_t volume; /* binary fraction / USHRT_MAX */
+	uint32_t chan_mask;
+	int bits;
+};
+
+static int inner_main(struct tone_generator_config config)
+{
+	struct pcm_config *pcm_config = &config.pcm_config;
+	struct pcm *pcm;
+	unsigned pos;
+	void *buf;
+
+	pcm = pcm_open(config.card, config.device, PCM_OUT, pcm_config);
+	if (!pcm) {
+		fprintf(stderr, "Could not open sound card\n");
+		fprintf(stderr, "%s\n", pcm_get_error(pcm));
+		return 1;
+	}
+	if (!pcm_is_ready(pcm)) {
+		fprintf(stderr, "Sound card not ready\n");
+		fprintf(stderr, "%s\n", pcm_get_error(pcm));
+		return 1;
+	}
+
+	buf = calloc(config.bits / 8,
+			pcm_config->period_size * pcm_config->channels);
+	if (!buf) {
+		fprintf(stderr, "Could not allocate memory for buffer\n");
+		return 1;
+	}
+
+	for (pos=0 ; (!config.duration || (pos < config.duration)) ; pos += pcm_config->period_size) {
+		oscillator_table_render(buf,
+			config.wave_table,
+			pos,
+			pcm_config->period_size,
+			config.wave_scale,
+			pcm_config->channels,
+			config.chan_mask, /* write to all channels */
+			config.volume,
+			config.bits);
+		if (pcm_write(pcm,
+			      buf,
+			      pcm_config->channels * pcm_config->period_size * (config.bits/8))) {
+			fprintf(stderr, "Error writing to sound card\n");
+			fprintf(stderr, "%s\n", pcm_get_error(pcm));
+			break;
+		}
+	}
+
+	pcm_close(pcm);
+
+	return 0;
+}
+
+static void usage()
+{
+	struct wave_table *ptr;
+
+	printf("Usage: audio-tool [options] tone <wave_type> <frequency> [<vol_db>]\n");
+	printf("\n");
+	printf("wave_type:\n");
+	for (ptr=g_wave_tables ; ptr->name ; ++ptr) {
+		printf("    %s\n", ptr->name);
+	}
+	printf("frequency: non-negative real number\n");
+	printf("vol_db: (optional) Volume attenuation in dB FS (implied negative, must be >= 0, default=0)\n");
+}
+
+int tone_generator_main(const struct audio_tool_config *at_config, int argc, char* argv[])
+{
+	struct tone_generator_config config = {
+		.card = 0,
+		.device = 0,
+		.chan_mask = ~0,
+	};
+	struct pcm_config pcm_config;
+	struct wave_table *ptr, *table;
+	struct wave_scale wave_scale;
+	double freq;
+	char *arg_wave_type, *arg_freq, *arg_voldb;
+	double tmp;
+
+	if ((argc < 3) || (argc > 4)) {
+		usage();
+		return 1;
+	}
+
+	if (check_wave_tables())
+		return 1;
+
+	arg_wave_type = argv[1];
+	arg_freq = argv[2];
+	if (argc > 3)
+		arg_voldb = argv[3];
+	else
+		arg_voldb = "0";
+
+	/* Set sane defaults */
+	memset(&pcm_config, 0, sizeof(struct pcm_config));
+	switch (at_config->bits) {
+	case 8: pcm_config.format = PCM_FORMAT_S8; break;
+	case 16: pcm_config.format = PCM_FORMAT_S16_LE; break;
+	case 24: pcm_config.format = PCM_FORMAT_S24_LE; break;
+	case 32: pcm_config.format = PCM_FORMAT_S32_LE; break;
+	default:
+		assert(0);
+	}
+
+	config.device = at_config->device;
+	config.card = at_config->card;
+	pcm_config.period_size = at_config->period_size;
+	pcm_config.period_count = at_config->num_periods;
+	pcm_config.rate = at_config->rate;
+	pcm_config.channels = at_config->channels;
+	config.chan_mask = at_config->channel_mask;
+	config.duration = at_config->duration * pcm_config.rate;
+	config.bits = at_config->bits;
+
+	for (ptr = g_wave_tables ; ptr->name ; ++ptr) {
+		if (strcmp(arg_wave_type, ptr->name) == 0) {
+			table = ptr;
+			assert( IS_POWER_OF_TWO(table->length) );
+			assert( table->mask == table->length - 1 );
+			break;
+		}
+	}
+	if (ptr->name == 0) {
+		fprintf(stderr, "Invalied wave_type parameter\n");
+		return 1;
+	}
+
+	tmp = atof(arg_freq);
+	if (tmp < 10.0) {
+		fprintf(stderr, "Error: frequency must be > 10Hz\n");
+		return 1;
+	}
+	freq = tmp;
+
+	tmp = atof(arg_voldb);
+	if (tmp < 0 ) {
+		fprintf(stderr, "Volume attenuation must be greater than 0 dB FS\n");
+		return 1;
+	}
+	/* Convert db to fraction */
+	tmp = -tmp;
+	tmp = pow(10.0, tmp/10.0);
+	config.volume = (unsigned short) (tmp * ((double)USHRT_MAX));
+
+	tmp = ((double)pcm_config.rate) / freq;
+	wave_scale.length = tmp;
+	tmp = (tmp - wave_scale.length) * 0xFFF;
+	wave_scale.sub = tmp;
+	wave_scale.sub_den = 0xFFF;
+	wave_scale.sub_shift = 12;
+
+	/* This restriction prevents overflows in render()
+	 */
+	{
+		uint16_t bits = 0;
+		while ((1<<bits) < table->length) ++bits;
+		if (wave_scale.sub_shift + bits > 24) {
+			fprintf(stderr, "bits(wave_scale) + bits(table.length) "
+				" must be less than or equal to 24\n");
+			return 1;
+		}
+	}
+
+	memcpy(&config.pcm_config, &pcm_config, sizeof(pcm_config));
+	memcpy(&config.wave_scale, &wave_scale, sizeof(wave_scale));
+	config.wave_table = table;
+
+	return inner_main(config);
+
+	return 0;
+}
diff --git a/audio-tool/tone-generator.h b/audio-tool/tone-generator.h
new file mode 100644
index 0000000..e4c6aee
--- /dev/null
+++ b/audio-tool/tone-generator.h
@@ -0,0 +1,31 @@
+/* Copyright (c) 2011, Gabriel M. Beddingfield <gabrbedd@gmail.com>
+ * 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.
+ *     * Neither the name of the <organization> nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 <COPYRIGHT HOLDER> 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.
+ */
+#ifndef __AUDIO_TOOL_TONE_GENERATOR_H__
+#define __AUDIO_TOOL_TONE_GENERATOR_H__
+
+int tone_generator_main(const struct audio_tool_config *config, int argc, char **argv);
+
+#endif /* __AUDIO_TOOL_TONE_GENERATOR_H__ */
diff --git a/audio-tool/wav_chan_splitter.c b/audio-tool/wav_chan_splitter.c
new file mode 100644
index 0000000..90ee406
--- /dev/null
+++ b/audio-tool/wav_chan_splitter.c
@@ -0,0 +1,138 @@
+/*
+ * wav_chan_splitter.c
+ *
+ * Utility for splitting a multichannel wav file to multiple mono wav files.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "wav_chan_splitter.h"
+
+#define MAX_FILENAME_LEN 256
+#define MAX_CHANNELS 8
+#define MAX_BYTES 4 // Up to 32-bit samples
+
+/* Reuse tinyplay/tinycap code for header parsing */
+static int parse_header (char * filename, wav_header_t * wav_header) {
+	FILE* file;
+	file = fopen(filename, "rb");
+	if (!file) {
+		fprintf(stderr, "Unable to open file '%s'\n", filename);
+		return 1;
+	}
+	fread(wav_header, sizeof(wav_header_t), 1, file);
+	if ((wav_header->riff_id != ID_RIFF) ||
+			(wav_header->riff_fmt != ID_WAVE) ||
+			(wav_header->data_id != ID_DATA)) {
+		fprintf(stderr, "Error: '%s' is not a PCM Wav file\n", filename);
+		fclose(file);
+		return 1;
+	}
+	fclose(file);
+	return 0;
+}
+
+/* Create mono header based on multichannel header  */
+static int create_new_header (wav_header_t * wav_header_out, wav_header_t * wav_header) {
+	if (!wav_header_out || !wav_header) {
+		fprintf(stderr, "Invalid argument for %s\n", __func__);
+		return 1;
+	}
+	memcpy(wav_header_out, wav_header, sizeof(wav_header_t));
+	wav_header_out->num_channels = 1;
+	wav_header_out->byte_rate = (wav_header->byte_rate)/(wav_header->num_channels);
+	wav_header_out->block_align = (wav_header->block_align)/(wav_header->num_channels);
+	wav_header_out->data_sz = (wav_header->data_sz)/(wav_header->num_channels);
+	/* Tinycap doesn't set riff_sz field, fill it up for the split files */
+	/* Definition: size of the rest of header (36 bytes) + datachunk size */
+	wav_header_out->riff_sz = wav_header_out->data_sz + 36;
+	return 0;
+}
+
+static int split (char *filename, const wav_header_t * wav_header, const wav_header_t * wav_header_out) {
+	char outname[MAX_FILENAME_LEN];
+	uint8_t i = 0;
+	char *extension_pos;
+	FILE *fp;                   /* input file pointer  */
+	FILE *fplist[MAX_CHANNELS]; /* output file pointers  */
+	uint8_t data[MAX_CHANNELS*MAX_BYTES];
+        size_t num_bytes_per_sample;
+
+	fp = fopen(filename, "rb");
+	/* Jump to data block  */
+	fseek(fp, sizeof(wav_header_t), SEEK_SET);
+
+	strncpy(outname, filename, sizeof(outname));
+	/* Get extension index of filename */
+	extension_pos = strrchr(outname, '.');
+	/* Point to '\0' instead if no extension is found */
+	if (!extension_pos)
+		extension_pos = strrchr(outname, '\0');
+	if (!extension_pos) {
+		fprintf(stderr, "Filename is too long!\n");
+		return 1;
+	}
+
+	printf("splitting...\n");
+
+	/* Create files <filename_[01 - N].wav> and copy headers */
+	for (i = 0; i < wav_header->num_channels; i++) {
+		snprintf(extension_pos, sizeof(outname) - (extension_pos - outname), "_%02d.wav", i + 1);
+		fplist[i] = fopen(outname, "wb");
+		fwrite(wav_header_out, sizeof(wav_header_t), 1, fplist[i]);
+	}
+
+        num_bytes_per_sample = wav_header_out->bits_per_sample >> 3;
+
+	/* Split data block */
+	while(fread(data, wav_header->block_align, 1, fp) == 1) {
+		for (i = 0; i < wav_header->num_channels; i++)
+			fwrite(&data[i*num_bytes_per_sample], num_bytes_per_sample, 1, fplist[i]);
+	}
+
+	for (i = 0; i < wav_header->num_channels; i++) {
+		fclose(fplist[i]);
+	}
+	fclose(fp);
+	return 0;
+}
+
+int split_main (int argc, char* argv[]) {
+	char *filename;
+	wav_header_t wav_header, wav_header_out;
+
+	if (argc != 2) {
+		fprintf(stderr, "Usage: audio-tool split file.wav\n");
+		return 1;
+	}
+	filename = argv[1];
+
+	if (parse_header(filename, &wav_header))
+		return 1;
+
+	printf("filename: %s\nformat: %d\nnum_channels: %d\nsample_rate: %d\nbyte_rate: %d\nblock_align: %d\nbits_per_sample: %d\n\n",
+		filename, wav_header.audio_format, wav_header.num_channels, wav_header.sample_rate,
+		wav_header.byte_rate, wav_header.block_align, wav_header.bits_per_sample);
+
+	if (wav_header.num_channels > MAX_CHANNELS) {
+		fprintf(stderr, "Error: only support %d or less channels\n", MAX_CHANNELS);
+		return 1;
+	}
+
+	if (wav_header.num_channels <= 1) {
+		fprintf(stderr, "Error: audio channel is already mono or invalid\n");
+		return 1;
+	}
+
+	if (create_new_header(&wav_header_out, &wav_header))
+		return 1;
+
+	if(split(filename, &wav_header, &wav_header_out))
+		return 1;
+
+	printf("Done!\n");
+	return 0;
+}
diff --git a/audio-tool/wav_chan_splitter.h b/audio-tool/wav_chan_splitter.h
new file mode 100644
index 0000000..fa63f9f
--- /dev/null
+++ b/audio-tool/wav_chan_splitter.h
@@ -0,0 +1,28 @@
+#ifndef __AUDIO_TOOL_WAV_CHAN_SPLITTER_H__
+#define __AUDIO_TOOL_WAV_CHAN_SPLITTER_H__
+
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT  0x20746d66
+#define ID_DATA 0x61746164
+
+typedef struct wav_header {
+        uint32_t riff_id;
+        uint32_t riff_sz;
+        uint32_t riff_fmt;
+        uint32_t fmt_id;
+        uint32_t fmt_sz;
+        uint16_t audio_format;
+        uint16_t num_channels;
+        uint32_t sample_rate;
+        uint32_t byte_rate;
+        uint16_t block_align;
+        uint16_t bits_per_sample;
+        uint32_t data_id;
+        uint32_t data_sz;
+} wav_header_t;
+
+int split_main (int argc, char* argv[]);
+
+#endif /* __AUDIO_TOOL_WAV_CHAN_SPLITTER_H__ */