Upgrade to Jellybean version of TinyHAL

This brings in the Jellybean version of TinyHAL. This is a
significant update compared to the Ice Cream Sandwich version.

- The audio_hw.c has been replaced by code based on asus/grouper
    so it is useful in real devices

- The config file parser + routing control has been completely
    rewritten and now builds to a separate library so that it can
    be used by alternative HALs without having to branch the source

- There are now more capabilities in the audio routing configuration:
    - Support for setting binary controls
    - Support for defining compressed streams
    - Support for defining custom streams for hardware-hardware links
    - You can now define a stream-specific set of control values for
        each device, in addition to the basic device enable/disable
        settings that were supported by the ICS version. This means
        that when a stream is connected to or disconnected from a
        device you can invoke control settings that are specific to
        that {stream:device} combination.
    - You can now define custom usecases within each stream, which
        the audio HAL can invoke by name. This allows custom use-cases
        without having to hardcode knowledge of all types of hardware
        into TinyHAL. The audio_hw.c file supplied with TinyHAL passes
        set_parameter() strings to this to allow you to define special
        control settings to apply when a parameter string has a certain
        value.
    - The file audio.example.xml contains documentation of the
        configuration file format and how to use the features.

The configuration file format has changed slightly to accommodate the
new features, and also to make it more regular and expandable. The
effort to convert an old config file to the new format is minimal.

Signed-off-by: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
diff --git a/Android.mk b/Android.mk
index a33b9ae..fb97c6b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,22 +1,84 @@
-ifeq  ($(strip $(BOARD_USES_TINY_AUDIO_HW)),true)
+#
+# Copyright (C) 2012 Wolfson Microelectronics plc
+# Copyright (C) 2011 The Android Open Source Project
+#
+# 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)
 
 include $(CLEAR_VARS)
 
-# Should change this so the enable variable gets used as the name?
-LOCAL_MODULE := audio.primary.herring
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
-LOCAL_SRC_FILES := audio_hw.c
-LOCAL_C_INCLUDES += \
-	external/tinyalsa/include \
-	external/expat/lib \
-	system/media/audio_utils/include \
-	system/media/audio_effects/include
-LOCAL_SHARED_LIBRARIES := liblog libcutils libtinyalsa libaudioutils \
-	libdl libexpat
+LOCAL_MODULE := libaudiohalcm
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)
 LOCAL_MODULE_TAGS := optional
 
+LOCAL_CFLAGS += -Werror
+
+LOCAL_C_INCLUDES += \
+	external/tinycompress/include \
+	external/tinyalsa/include \
+	external/tinyhal/audio \
+	external/expat/lib \
+	$(call include-path-for, audio-utils)
+
+LOCAL_SRC_FILES := \
+	audio_config.c
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	libutils \
+	libdl	\
+	liblog	\
+	libexpat	\
+	libtinyalsa	\
+
+include $(BUILD_SHARED_LIBRARY)
+
+ifeq ($(strip $(BOARD_USES_TINYHAL_AUDIO)),true)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := audio.primary.$(TARGET_DEVICE)
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_CFLAGS += -Werror
+
+LOCAL_C_INCLUDES += \
+	external/tinycompress/include \
+	external/tinyalsa/include \
+	external/tinyhal/audio \
+	external/expat/lib \
+	$(call include-path-for, audio-utils)
+
+LOCAL_SRC_FILES := \
+	audio_hw.c		\
+	voice_trigger.cpp
+
+LOCAL_STATIC_LIBRARIES := \
+	libmedia_helper
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	libutils \
+	libdl	\
+	liblog	\
+	libhardware_legacy \
+	libtinyalsa	\
+	libtinycompress	\
+	libaudiohalcm \
+	libaudioutils \
+	libsysutils
+
 include $(BUILD_SHARED_LIBRARY)
 
 endif
diff --git a/audio.example.xml b/audio.example.xml
new file mode 100644
index 0000000..4b74272
--- /dev/null
+++ b/audio.example.xml
@@ -0,0 +1,264 @@
+<!-- This file is provided as a reference for writing config files
+It does NOT represent any particular device and will NOT work on
+real hardware. You must create a file with the correct settings for
+your hardware, and the comments here will explain the layout of this
+file and expected content.
+
+On the target device this file must be located in /system/etc and named
+audio.<device>.xml, where <device> is the string returned by the system
+property ro.product.device
+-->
+
+<audiohal>
+    <!-- mixer element _must_ be first. The 'card' attribute is optional
+    and sets the ALSA card number of the mixer device - if not given it
+    defaults to 0 -->
+
+	<mixer card="0">
+
+        <!-- init element lists control settings required to initialize the
+        hardware and driver. These settings are applied only once when the
+        library is first loaded during boot -->
+
+        <init>
+
+            <!-- A sequence of ctl elements. Each entry sets a mixer
+            control to a given value. The entries are applied in the
+            order they are listed here.
+            Each entry _must_ have these attributes
+                name - name of the ALSA mixer control
+                val  - value to set it to
+
+            It can also have an index attribute giving the numeric index
+            of the control value to set. This is used where a control
+            has multiple value slots (for example a volume control
+            with two values, one for left channel and one for right), If
+            an index attribute is not given the content of the val attribute
+            will be applied to all value slots of the control.
+
+            The numbers in val and index attributes can be given in either
+            decimal, or hex (hex is prefixed with 0x). For a control with
+            enumerated values the val attribute must be a string
+
+            BYTE ARRAYS: for controls that are a byte array val must be
+            a string of comma-separated byte values. This can be shorter
+            than the total size of the control, combined with the
+            optional index attribute this allows any subset of the byte
+            array to be changed.
+            -->
+
+            <ctl name="DAC1 Switch" val="1" />
+            <ctl name="Speaker Enable" val="0"/>
+            <ctl name="Jack Enable" val="0"/>
+            <ctl name="Codec Config" index="8" val="0x7f,0x54,0xaa,0xaa"/>
+
+        </init>
+	</mixer>
+
+<!-- Next you must list all the devices supported by the hardware. The
+name attribute of the <device> element identifies the device. These names are
+recognized:
+    "global"        dummy global device - see explanation below
+    "speaker"       AUDIO_DEVICE_OUT_SPEAKER
+    "earpiece"      AUDIO_DEVICE_OUT_EARPIECE
+    "headset"       AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_IN_WIRED_HEADSET
+    "headphone"     AUDIO_DEVICE_OUT_WIRED_HEADPHONE
+    "sco"           AUDIO_DEVICE_OUT_ALL_SCO | AUDIO_DEVICE_IN_ALL_SCO
+    "a2dp"          AUDIO_DEVICE_OUT_ALL_A2DP
+    "usb"           AUDIO_DEVICE_OUT_ALL_USB
+    "mic"           AUDIO_DEVICE_IN_BUILTIN_MIC
+    "back mic"      AUDIO_DEVICE_IN_BACK_MIC
+    "voice"         AUDIO_DEVICE_IN_VOICE_CALL
+    "aux"           AUDIO_DEVICE_IN_AUX_DIGITAL
+
+Within the <device> element you can declare a number of "paths", each path
+defines a group of control settings to be applied. Each path is identified by
+a name. The "on" and "off" paths are special and list a global enable and
+disable setting for the device. Use of devices is reference-counted, when
+routing of a stream is changed to use a device that is currently disabled its
+"on" path will be applied. When no streams are using a device its "off"
+path will be applied.
+
+Other paths are user-defined and you can give them any name you choose.
+They are used to apply custom paths when required by a stream and will
+be used only when a stream is connected to or disconnected from a
+device if the <stream> element has an <enable> or <disable> element requesting
+that path.
+
+It is not mandatory to provide paths. You only need to define paths
+if there are specific control settings that must be applied. So for example
+if no controls need be applied to enable or disable a device then you
+do not need to define the "on" and "off" paths.
+
+The <ctl> elements within each path have the same format and behaviour
+as described under <mixer><init>.
+
+The "global" device is a special device the represents the audio system as a
+whole and is used to invoke mixer settings that are independent of any real
+device and which apply globally to the audio system. A stream is automatically
+connected to "global" when it is opened and disconnected when it is closed.
+The behaviour is identical to the way paths are invoked in any other <device>
+entry so the effect is
+    - the "on" path will be applied when a stream is opened and there are
+        no other streams already open. As an example this could be used to
+        bring the audio hardware out of a standby state
+
+    - the "off" path will be applied when the last open stream is closed.
+        As an example this could be used to put the audio hardware into a
+        standby state
+
+    - the custom paths will be applied when the stream that requests them
+        is opened or closed.
+-->
+
+	<device name="speaker">
+        <path name="on">
+            <!-- List of ctl element for control values to apply
+            when this device is enabled -->
+            <ctl name="Speaker Enable" val="1"/>
+        </path>
+
+        <path name="off">
+            <!-- List of ctl element for control values to apply
+            when this device is disabled -->
+            <ctl name="Speaker Enable" val="0"/>
+        </path>
+
+        <!-- Following paths are user-defined and are applied when a
+        <stream> elements' routing is changed to add or remove this
+        device. If the path name matches the name given in the <stream>
+        element it will be applied. A stream could be routed to multiple
+        inputs or outputs - the paths for connecting and disconnecting
+        a stream to a device must therefore have the same name in each
+        <device>.
+
+        It is not mandatory to declare custom paths - depending on your
+        hardware there may not be any specific action required to route
+        a stream to a particular device. Also you do not have to define
+        the path in every device, only the devices where some action must
+        be taken to connect or disconnect a stream.
+
+        For this example four custom paths are defined:
+            pcm_out_en = control setting to connect PCM output to this device
+            pcm_out_dis = control setting to disconnect PCM output from this device
+         -->
+
+        <path name="pcm_out_en">
+            <ctl name="PCM Speaker switch" val="1"/>
+        </path>
+        <path name="pcm_out_dis">
+            <ctl name="PCM Speaker switch" val="0"/>
+        </path>
+	</device>
+
+	<device name="headphone">
+        <path name="on">
+            <ctl name="Jack Enable" val="1"/>
+        </path>
+        <path name="off">
+            <ctl name="Jack Enable" val="0"/>
+        </path>
+        <path name="pcm_out_en">
+            <ctl name="PCM Jack switch" val="1"/>
+        </path>
+        <path name="pcm_out_dis">
+            <ctl name="PCM Jack switch" val="0"/>
+        </path>
+	</device>
+
+<!-- Following the device definitions there must be a <stream> entry
+for every output and input stream supported by the hardware.
+There are two types of stream that can be declared here:
+- anonymous streams, these will be used to supply playback and record
+    streams for AudioFlinger
+
+- named streams, which are mainly used to declare custom streams to handle
+    special routing use-cases that are external to the normal Android audio
+    path - typically where the audio is routed entirely in hardware without
+    being passed through Android, for example the baseband audio link or
+    FM radio.
+
+For standard anonymous streams there are two that would normally be on
+any device: PCM input and PCM output. It is also possible to declare a stream
+as "compress" - this is intended for cases where a playback stream is
+decompressed in hardware, or a record stream provides raw compressed data that
+must be decompressed in software.
+
+Named streams can be declared as type "hw", to represent a hardware-hardware
+link where specifying a data type and direction would be meaningless.
+
+Mandatory attributes for PCM and compressed streams:
+    type    must be "pcm" or "compress"
+    dir     direction of stream, either "in" (recording) or "out" (playback)
+
+Mandatory for named streams:
+    type    must be "pcm", "compress" or "hw"
+    name    a custom name for a named stream. The name you choose here must
+                match the name your HAL will use to request this stream
+
+Mandatory for hw streams:
+    type    must be "hw"
+    name    a custom name for the stream (hw streams must be named streams)
+
+Optional attributes:
+    card    ALSA card number. If not given this defaults to 0
+    device  ALSA device number. If not given this defaults to 0
+    instances   limits the maximum number of instances of this stream, if not
+                specified the number of instances is unlimited
+    name    a custom name for a named stream. The name you choose here must
+                match the name your HAL will use to request this stream
+
+Anonymous PCM streams should not normally have an instance limit.
+-->
+
+    <stream type="pcm" dir="out" card="0" device="0">
+        <!-- The <enable> and <disable> tags give the name of a path
+        to apply for each connected device when the stream is either connected
+        to (enable) or disconnected from (disable) that device.
+        The way this works is that when stream routing changes, the HAL will
+        look through the paths of each device this stream is connected to,
+            - for each device the stream is being disconnected from, if it
+                contains a path matching the path name in <disable>, that path
+                will be applied.
+            - for each device the stream is being connected to, if it
+                contains a path matching the path name in <enable>, that path
+                will be applied.
+        -->
+        <enable path="pcm_out_en"/>
+        <disable path="pcm_out_dis"/>
+
+        <!-- The optional usecase block allows you to define custom use-cases that
+        are triggered by set_parameter() calls to the HAL. The set_parameter()
+        is a string of the form <setting>=<value>. The HAL will search for a
+        usecase block whose name attribute matches <setting> and within that
+        a case block whose name attribute matches <value>. If a matching case
+        block is found the enclosed <ctl> blocks will be applied.
+        The example below defines a use case for switching a codec algorithm
+        between wideband and narrowband response. The two cases will be
+        triggered by a set_parameter() of "bandwidth=narrow" or "bandwidth=wide".
+        -->
+        <usecase name="bandwidth">
+            <case name="narrow">
+                <ctl name="Codec Wideband" val="0" />
+            </case>
+            <case name="wide">
+                <ctl name="Codec Wideband" val="1" />
+            </case>
+        </usecase>
+
+    </stream>
+
+    <stream type="pcm" dir="in" card="0" device="0">
+    </stream>
+
+    <!-- Example named stream, in this case for an FM radio path . This will not
+    be available for standard AudioFlinger playback and record paths. It must
+    be explicitly requested by the audio HAL when FM radio is enabled
+    -->
+    <stream name="FM radio" type="pcm" dir="in" card="0" device="0">
+        <!-- control paths to be enabled when this stream is enabled or disabled -->
+        <enable path="fm_radio_en"/>
+        <disable path="fm_radio_dis"/>
+    </stream>
+
+</audiohal>
diff --git a/audio_config.c b/audio_config.c
new file mode 100644
index 0000000..4d2dec3
--- /dev/null
+++ b/audio_config.c
@@ -0,0 +1,2085 @@
+/*
+ * Copyright (C) 2012-2013 Wolfson Microelectronics plc
+ *
+ * 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.
+ */
+
+#define LOG_TAG "tiny_hal_config"
+/*#define LOG_NDEBUG 0*/
+/*#undef NDEBUG*/
+
+#include <stddef.h>
+#include <errno.h>
+#include <assert.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <cutils/compiler.h>
+
+#include <system/audio.h>
+
+/* Workaround for linker error if audio_effect.h is included in multiple
+ * source files. Prevent audio.h including it */
+#define ANDROID_AUDIO_EFFECT_H
+struct effect_interface_s;
+typedef struct effect_interface_s **effect_handle_t;
+#include <hardware/audio.h>
+
+#include <tinyalsa/asoundlib.h>
+#include <expat.h>
+
+#include "audio_config.h"
+
+#define MIXER_CARD_DEFAULT 0
+#define PCM_CARD_DEFAULT 0
+#define PCM_DEVICE_DEFAULT 0
+#define COMPRESS_CARD_DEFAULT 0
+#define COMPRESS_DEVICE_DEFAULT 0
+
+/* The dynamic arrays are extended in multiples of this number of objects */
+#define DYN_ARRAY_GRANULE 16
+
+/* Largest byte array control we handle */
+#define BYTE_ARRAY_MAX_SIZE 512
+
+#define INVALID_CTL_INDEX 0xFFFFFFFFUL
+
+struct config_mgr;
+struct stream;
+struct path;
+struct device;
+struct usecase;
+struct scase;
+
+/* Dynamically extended array of fixed-size objects */
+struct dyn_array {
+    uint                count;
+    uint16_t            max_count; /* current maximum size of allocated array */
+    uint16_t            elem_size;  /* size of array elements */
+    union {
+        void               *data;
+        struct device      *devices;
+        struct stream      *streams;
+        struct path        *paths;
+        struct usecase     *usecases;
+        struct scase       *cases;
+        struct ctl         *ctls;
+        const char         **path_names;
+    };
+};
+
+/* Paths for "on" and "off" are a special case and have fixed ids */
+enum {
+    e_path_id_off = 0,
+    e_path_id_on = 1,
+    e_path_id_custom_base = 2
+};
+
+struct ctl {
+    struct mixer_ctl    *ctl;
+    uint32_t            index;
+    uint32_t            array_count;
+    union {
+        uint32_t        uinteger;
+        const char      *name;
+        const uint8_t   *data;
+    } value;
+};
+
+struct path {
+    int                 id;         /* Integer identifier of this path */
+    struct dyn_array    ctl_array;
+};
+
+struct device {
+    uint32_t    type;               /* 0 is reserved for the global device */
+    int         use_count;          /* counts total streams using this device */
+    struct dyn_array path_array;
+};
+
+struct scase {
+    const char          *name;
+    struct dyn_array    ctl_array;
+};
+
+struct usecase {
+    const char          *name;
+    struct dyn_array    case_array;
+};
+
+struct stream_control {
+    struct mixer_ctl    *ctl;
+    uint                id;
+};
+
+struct stream {
+    struct hw_stream  info;   /* must be first member */
+
+    struct config_mgr*  cm;
+    const char* name;       /* used for named custom streams */
+
+    int     ref_count;
+    int     max_ref_count;
+
+    int     enable_path;    /* id of paths to invoke when enabled */
+    int     disable_path;   /* id of paths to invoke when disabled */
+
+    uint32_t current_devices;   /* devices currently active for this stream */
+
+    struct {
+        struct stream_control volume_left;
+        struct stream_control volume_right;
+    } controls;
+
+    struct dyn_array    usecase_array;
+};
+
+struct config_mgr {
+    pthread_mutex_t lock;
+
+    struct mixer    *mixer;
+
+    uint32_t        supported_output_devices;
+    uint32_t        supported_input_devices;
+
+    struct dyn_array device_array;
+    struct dyn_array stream_array;
+    struct dyn_array named_stream_array;
+};
+
+/*********************************************************************
+ * Structures and enums for XML parser
+ *********************************************************************/
+
+#define MAX_PARSE_DEPTH     6
+
+/* For faster parsing put more commonly-used elements first */
+enum element_index {
+    e_elem_ctl = 0,
+    e_elem_path,
+    e_elem_device,
+    e_elem_stream,
+    e_elem_enable,
+    e_elem_disable,
+    e_elem_case,
+    e_elem_usecase,
+    e_elem_stream_ctl,
+    e_elem_init,
+    e_elem_mixer,
+    e_elem_audiohal,
+
+    e_elem_count
+};
+
+/* For faster parsing put more commonly-used attribs first */
+enum attrib_index {
+    e_attrib_name = 0,
+    e_attrib_val,
+    e_attrib_path,
+    e_attrib_function,
+    e_attrib_type,
+    e_attrib_index,
+    e_attrib_dir,
+    e_attrib_card,
+    e_attrib_device,
+    e_attrib_instances,
+    e_attrib_rate,
+    e_attrib_period_size,
+    e_attrib_period_count,
+
+    e_attrib_count
+};
+
+#define BIT(x)     (1<<(x))
+
+struct parse_state;
+typedef int(*elem_fn)(struct parse_state *state);
+
+struct parse_element {
+    const char      *name;
+    uint16_t        valid_attribs;  /* bitflags of valid attribs for this element */
+    uint16_t        required_attribs;   /* bitflags of attribs that must be present */
+    uint16_t        valid_subelem;  /* bitflags of valid sub-elements */
+    elem_fn         start_fn;
+    elem_fn         end_fn;
+};
+
+struct parse_attrib {
+    const char      *name;
+};
+
+struct parse_device {
+    const char      *name;
+    uint32_t        device;
+};
+
+struct parse_stack_entry {
+    uint16_t            elem_index;
+    uint16_t            valid_subelem;
+};
+
+/* Temporary state info for config file parser */
+struct parse_state {
+    struct config_mgr   *cm;
+    FILE                *file;
+    XML_Parser          parser;
+    char                read_buf[256];
+    int                 parse_error; /* value >0 aborts without error */
+    int                 error_line;
+    int                 mixer_card_number;
+
+    struct {
+        const char      *value[e_attrib_count];
+        const XML_Char  **all;
+    } attribs;
+
+    /* Current parent object. We don't need a stack because
+     * an object cannot be nested below an object of the same type
+     * so there can only ever be zero or one object of a given type
+     * active at any time
+     */
+    struct {
+        struct device   *device;
+        struct stream   *stream;
+        struct path     *path;
+        struct usecase  *usecase;
+        struct scase    *scase;
+    } current;
+
+    /* This array hold a de-duplicated list of all path names encountered */
+    struct dyn_array    path_name_array;
+
+    /* This is a temporary path object used to collect the initial
+     * mixer setup control settings under <mixer><init>
+     */
+    struct path         init_path;
+
+    struct {
+        int             index;
+        struct parse_stack_entry entry[MAX_PARSE_DEPTH];
+    } stack;
+};
+
+
+static const char *debug_device_to_name(uint32_t device);
+
+/*********************************************************************
+ * Routing control
+ *********************************************************************/
+
+static void apply_ctls_l( struct ctl *pctl, const int ctl_count )
+{
+    int i;
+    unsigned int vnum;
+    unsigned int value_count;
+    int err = 0;
+    uint8_t ctl_data[BYTE_ARRAY_MAX_SIZE];
+
+    ALOGV("+apply_ctls_l");
+
+    for (i = 0; i < ctl_count; ++i, ++pctl) {
+        switch (mixer_ctl_get_type(pctl->ctl)) {
+            case MIXER_CTL_TYPE_BOOL:
+            case MIXER_CTL_TYPE_INT:
+                value_count = mixer_ctl_get_num_values(pctl->ctl);
+
+                ALOGV("apply ctl '%s' = 0x%x (%d values)",
+                                        mixer_ctl_get_name(pctl->ctl),
+                                        pctl->value.uinteger,
+                                        value_count);
+
+                if (pctl->index == INVALID_CTL_INDEX) {
+                    for (vnum = 0; vnum < value_count; ++vnum) {
+                        err = mixer_ctl_set_value(pctl->ctl, vnum, pctl->value.uinteger);
+                        if (err < 0) {
+                            break;
+                        }
+                    }
+                } else {
+                    err = mixer_ctl_set_value(pctl->ctl, pctl->index, pctl->value.uinteger);
+                }
+                ALOGE_IF(err < 0, "Failed to set ctl '%s' to %u",
+                                        mixer_ctl_get_name(pctl->ctl),
+                                        pctl->value.uinteger);
+                break;
+
+            case MIXER_CTL_TYPE_BYTE:
+                /* byte array */
+                vnum = mixer_ctl_get_num_values(pctl->ctl);
+
+                ALOGV("apply ctl '%s' = byte data (%d bytes)",
+                                        mixer_ctl_get_name(pctl->ctl),
+                                        vnum);
+
+                if ((pctl->index == 0) && (pctl->array_count == vnum)) {
+                    err = mixer_ctl_set_array(pctl->ctl, pctl->value.data, pctl->array_count);
+                } else {
+                    /* read-modify-write */
+                    err = mixer_ctl_get_array(pctl->ctl, ctl_data, vnum);
+                    if (err >= 0) {
+                        memcpy(&ctl_data[pctl->index], pctl->value.data, pctl->array_count);
+                        err = mixer_ctl_set_array(pctl->ctl, ctl_data, vnum);
+                    }
+                }
+
+                ALOGE_IF(err < 0, "Failed to set ctl '%s'",
+                                            mixer_ctl_get_name(pctl->ctl));
+                break;
+
+            case MIXER_CTL_TYPE_ENUM:
+                ALOGV("apply ctl '%s' to '%s'",
+                                            mixer_ctl_get_name(pctl->ctl),
+                                            pctl->value.name);
+
+                err = mixer_ctl_set_enum_by_string(pctl->ctl, pctl->value.name);
+
+                ALOGE_IF(err < 0, "Failed to set ctl '%s' to '%s'",
+                                            mixer_ctl_get_name(pctl->ctl),
+                                            pctl->value.name);
+                break;
+
+            default:
+                break;
+        }
+    }
+
+    ALOGV("-apply_ctls_l");
+}
+
+static void apply_path_l(struct path *path)
+{
+    ALOGV("+apply_path_l(%p) id=%u", path, path->id);
+
+    apply_ctls_l(path->ctl_array.ctls, path->ctl_array.count);
+
+    ALOGV("-apply_path_l(%p)", path);
+}
+
+static void apply_device_path_l(struct device *pdev, struct path *path)
+{
+    ALOGV("+apply_device_path_l(%p) id=%u", path, path->id);
+
+    /* The on and off paths for a device are reference-counted */
+    switch (path->id) {
+    case e_path_id_off:
+        if (--pdev->use_count > 0) {
+            ALOGV("Device still in use - not applying 'off' path");
+            return;
+        }
+        break;
+
+     case e_path_id_on:
+        if (++pdev->use_count > 1) {
+            ALOGV("Device already enabled - not applying 'on' path");
+            return;
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    apply_path_l(path);
+
+    ALOGV("-apply_device_path_l(%p)", path);
+}
+
+static void apply_paths_by_id_l(struct device *pdev, int first_id,
+                                int second_id)
+{
+    struct path *ppath = pdev->path_array.paths;
+    struct path *found_paths[2] = {0};
+    int path_count = pdev->path_array.count;
+
+    ALOGV("Applying paths [first=%u second=%u] to device(@%p, mask=0x%x '%s')",
+                first_id, second_id, ppath, pdev->type, debug_device_to_name(pdev->type));
+
+    /* To save time we find both paths in a single walk of the list */
+    for (; path_count > 0; --path_count, ++ppath) {
+        if (ppath->id == first_id) {
+            found_paths[0] = ppath;
+            if ((found_paths[1] != NULL) || (first_id == second_id)) {
+                /* We have both paths or there is only one path to find */
+                break;
+            }
+        } else if (ppath->id == second_id) {
+            found_paths[1] = ppath;
+            if (found_paths[0] != NULL) {
+                break;
+            }
+        }
+    }
+
+    if (found_paths[0] != NULL) {
+        apply_device_path_l(pdev, found_paths[0]);
+    }
+
+    if (found_paths[1] != NULL) {
+        apply_device_path_l(pdev, found_paths[1]);
+    }
+}
+
+static void apply_paths_to_devices_l(struct config_mgr *cm, uint32_t devices,
+                                    int first_id, int second_id)
+{
+    struct device *pdev = cm->device_array.devices;
+    int dev_count = cm->device_array.count;
+    const uint32_t input_flag = devices & AUDIO_DEVICE_BIT_IN;
+
+    /* invoke path path_id on all struct device matching devices */
+    ALOGV("Apply paths [first=%u second=%u] to devices in 0x%x",
+            first_id, second_id, devices);
+
+    devices &= ~AUDIO_DEVICE_BIT_IN;
+
+    while ((dev_count > 0) && (devices != 0)) {
+        if (((pdev->type & input_flag) == input_flag)
+                    && ((pdev->type & devices) != 0)) {
+            devices &= ~pdev->type;
+            apply_paths_by_id_l(pdev, first_id, second_id);
+        }
+
+        --dev_count;
+        ++pdev;
+    }
+}
+
+static void apply_paths_to_global_l(struct config_mgr *cm,
+                                    int first_id, int second_id)
+{
+    struct device *pdev = cm->device_array.devices;
+    struct device * const pend = pdev + cm->device_array.count;
+
+    ALOGV("Apply global paths [first=%u second=%u]", first_id, second_id);
+
+    while (pdev < pend) {
+        if (pdev->type == 0) {
+            apply_paths_by_id_l(pdev, first_id, second_id);
+            break;
+        }
+        ++pdev;
+    }
+}
+
+uint32_t get_current_routes( const struct hw_stream *stream )
+{
+    struct stream *s = (struct stream *)stream;
+    ALOGV("get_current_routes(%p) 0x%x", stream, s->current_devices);
+    return s->current_devices;
+}
+
+void apply_route( const struct hw_stream *stream, uint32_t devices )
+{
+    struct stream *s = (struct stream *)stream;
+    struct config_mgr *cm = s->cm;
+
+    /* Only apply routes to devices that have changed state on this stream */
+    uint32_t enabling = devices & ~s->current_devices;
+    uint32_t disabling = ~devices & s->current_devices;
+
+    ALOGV("apply_route(%p) devices=0x%x", stream, devices);
+
+    if (stream_is_input(stream)) {
+        devices &= AUDIO_DEVICE_IN_ALL;
+        devices |= AUDIO_DEVICE_BIT_IN;
+    } else {
+        devices &= AUDIO_DEVICE_OUT_ALL;
+    }
+
+    pthread_mutex_lock(&cm->lock);
+
+    apply_paths_to_devices_l(cm, disabling, s->disable_path, e_path_id_off);
+    apply_paths_to_devices_l(cm, enabling, e_path_id_on, s->enable_path);
+
+    /* Save new set of devices for this stream */
+    s->current_devices = devices;
+
+    pthread_mutex_unlock(&cm->lock);
+}
+
+uint32_t get_routed_devices( const struct hw_stream *stream )
+{
+    struct stream *s = (struct stream *)stream;
+    return s->current_devices;
+}
+
+void rotate_routes( struct config_mgr *cm, int orientation )
+{
+    /* Route rotation not currently supported */
+}
+
+/*********************************************************************
+ * Stream control
+ *********************************************************************/
+
+int set_hw_volume( const struct hw_stream *stream, float left, float right)
+{
+    struct stream *s = (struct stream *)stream;
+    int pc;
+    int ret = -ENOSYS;
+
+    if (s->controls.volume_left.ctl) {
+        pc = (int)(left * 100);
+        mixer_ctl_set_percent(s->controls.volume_left.ctl,
+                                s->controls.volume_left.id,
+                                pc);
+        ret = 0;
+    }
+    if (s->controls.volume_right.ctl) {
+        pc = (int)(right * 100);
+        mixer_ctl_set_percent(s->controls.volume_right.ctl,
+                                s->controls.volume_right.id,
+                                pc);
+        ret = 0;
+    }
+
+    ALOGV_IF(ret == 0, "set_hw_volume: L=%f R=%f", left, right);
+
+    return ret;
+}
+
+static struct stream *find_named_stream(struct config_mgr *cm,
+                                   const char *name)
+{
+    struct stream *s = cm->named_stream_array.streams;
+    int i;
+
+    for (i = cm->named_stream_array.count - 1; i >= 0; --i) {
+        if (strcmp(s->name, name) == 0) {
+            return s;
+        }
+        s++;
+    }
+    return NULL;
+}
+
+const struct hw_stream *get_stream(struct config_mgr *cm,
+                                   const audio_devices_t devices,
+                                   const audio_output_flags_t flags,
+                                   const struct audio_config *config )
+{
+    int i;
+    struct stream *s = cm->stream_array.streams;
+    const bool pcm = audio_is_linear_pcm(config->format);
+    enum stream_type type;
+
+    ALOGV("+get_stream devices=0x%x flags=0x%x format=0x%x",
+                            devices, flags, config->format );
+
+    if (devices & AUDIO_DEVICE_BIT_IN) {
+        type = pcm ? e_stream_in_pcm : e_stream_in_compress;
+    } else {
+        type = pcm ? e_stream_out_pcm : e_stream_out_compress;
+    }
+
+    pthread_mutex_lock(&cm->lock);
+    for (i = cm->stream_array.count - 1; i >= 0; --i) {
+        ALOGV("get_stream: require type=%d; try type=%d refcount=%d refmax=%d",
+                    type, s[i].info.type, s[i].ref_count, s[i].max_ref_count );
+        if (s[i].info.type == type) {
+            if (s[i].ref_count < s[i].max_ref_count) {
+                ++s[i].ref_count;
+                if (s[i].ref_count == 1) {
+                    apply_paths_to_global_l(cm, e_path_id_on, s[i].enable_path);
+                }
+                break;
+            }
+        }
+    }
+
+    pthread_mutex_unlock(&cm->lock);
+
+    if (i >= 0) {
+        ALOGV("-get_stream =%p (refcount=%d)", &s[i].info,
+                                                s[i].ref_count );
+        return &s[i].info;
+    } else {
+        ALOGE("-get_stream no suitable stream" );
+        return NULL;
+    }
+}
+
+const struct hw_stream *get_named_stream(struct config_mgr *cm,
+                                   const char *name)
+{
+    int i;
+    struct stream *s;
+
+    ALOGV("+get_named_stream '%s'", name);
+
+    /* Streams can't be deleted so don't need to hold the lock during search */
+    s = find_named_stream(cm, name);
+
+    pthread_mutex_lock(&cm->lock);
+    if (s != NULL) {
+        if (s->ref_count < s->max_ref_count) {
+            ++s->ref_count;
+            if (s->ref_count == 1) {
+                apply_paths_to_global_l(cm, e_path_id_on, s->enable_path);
+            }
+        } else {
+            ALOGV("stream '%s' at maximum refcount %d", name, s->ref_count);
+            s = NULL;
+        }
+    }
+    pthread_mutex_unlock(&cm->lock);
+
+    if (s != NULL) {
+        ALOGV("-get_named_stream =%p (refcount=%d)", &s->info, s->ref_count );
+        return &s->info;
+    } else {
+        ALOGE("-get_named_stream no suitable stream" );
+        return NULL;
+    }
+}
+
+bool is_named_stream_defined(struct config_mgr *cm, const char *name)
+{
+    struct stream *s;
+
+    /* Streams can't be deleted so don't need to hold the lock during search */
+    s = find_named_stream(cm, name);
+
+    ALOGV("is_named_stream_defined '%s' = %d", name, (s != NULL));
+    return (s != NULL);
+}
+
+void release_stream( const struct hw_stream* stream )
+{
+    struct stream *s = (struct stream *)stream;
+
+    ALOGV("release_stream %p", stream );
+
+    if (s) {
+        pthread_mutex_lock(&s->cm->lock);
+        if (--s->ref_count == 0) {
+            /* Ensure all paths it was using are disabled */
+            apply_paths_to_devices_l(s->cm, s->current_devices,
+                                    e_path_id_off, s->disable_path);
+            apply_paths_to_global_l(s->cm, s->disable_path, e_path_id_off);
+        }
+        s->current_devices = 0;
+        pthread_mutex_unlock(&s->cm->lock);
+    }
+}
+
+uint32_t get_supported_output_devices( struct config_mgr *cm )
+{
+    const uint32_t d = cm->supported_output_devices;
+
+    ALOGV("get_supported_output_devices=0x%x", d);
+    return d;
+}
+
+uint32_t get_supported_input_devices( struct config_mgr *cm )
+{
+    const uint32_t d = cm->supported_input_devices;
+
+    ALOGV("get_supported_input_devices=0x%x", d);
+    return d;
+}
+
+/*********************************************************************
+ * Use-case control
+ *********************************************************************/
+int apply_use_case( const struct hw_stream* stream,
+                    const char *setting,
+                    const char *case_name)
+{
+    struct stream *s = (struct stream *)stream;
+    struct usecase *puc = s->usecase_array.usecases;
+    int usecase_count = s->usecase_array.count;
+    struct scase *pcase;
+    int case_count;
+    int ret;
+
+    ALOGV("apply_use_case(%p) %s=%s", stream, setting, case_name);
+
+    for (; usecase_count > 0; usecase_count--, puc++) {
+        if (0 == strcmp(puc->name, setting)) {
+            pcase = puc->case_array.cases;
+            case_count = puc->case_array.count;
+            for(; case_count > 0; case_count--, pcase++) {
+                if (0 == strcmp(pcase->name, case_name)) {
+                    pthread_mutex_lock(&s->cm->lock);
+                    apply_ctls_l(pcase->ctl_array.ctls, pcase->ctl_array.count);
+                    pthread_mutex_unlock(&s->cm->lock);
+                    ret = 0;
+                    goto exit;
+                }
+            }
+        }
+    }
+
+    ret = -ENOSYS;      /* use-case not implemented */
+
+exit:
+    return ret;
+}
+
+/*********************************************************************
+ * Config file parsing
+ *
+ * To keep this simple we restrict the order that config file entries
+ * may appear:
+ * - The <mixer> section must always appear first
+ * - Paths must be defined before they can be referred to
+ *********************************************************************/
+static int parse_mixer_start(struct parse_state *state);
+static int parse_device_start(struct parse_state *state);
+static int parse_device_end(struct parse_state *state);
+static int parse_stream_start(struct parse_state *state);
+static int parse_stream_end(struct parse_state *state);
+static int parse_stream_ctl_start(struct parse_state *state);
+static int parse_path_start(struct parse_state *state);
+static int parse_path_end(struct parse_state *state);
+static int parse_case_start(struct parse_state *state);
+static int parse_case_end(struct parse_state *state);
+static int parse_usecase_start(struct parse_state *state);
+static int parse_usecase_end(struct parse_state *state);
+static int parse_enable_start(struct parse_state *state);
+static int parse_disable_start(struct parse_state *state);
+static int parse_ctl_start(struct parse_state *state);
+static int parse_init_start(struct parse_state *state);
+
+static const struct parse_element elem_table[e_elem_count] = {
+    [e_elem_ctl] =    {
+        .name = "ctl",
+        .valid_attribs = BIT(e_attrib_name) | BIT(e_attrib_val) | BIT(e_attrib_index),
+        .required_attribs = BIT(e_attrib_name) | BIT(e_attrib_val),
+        .valid_subelem = 0,
+        .start_fn = parse_ctl_start,
+        .end_fn = NULL
+        },
+
+    [e_elem_path] =    {
+        .name = "path",
+        .valid_attribs = BIT(e_attrib_name),
+        .required_attribs = BIT(e_attrib_name),
+        .valid_subelem = BIT(e_elem_ctl),
+        .start_fn = parse_path_start,
+        .end_fn = parse_path_end
+        },
+
+    [e_elem_device] =    {
+        .name = "device",
+        .valid_attribs = BIT(e_attrib_name),
+        .required_attribs = BIT(e_attrib_name),
+        .valid_subelem = BIT(e_elem_path),
+        .start_fn = parse_device_start,
+        .end_fn = parse_device_end
+        },
+
+    [e_elem_stream] =    {
+        .name = "stream",
+        .valid_attribs = BIT(e_attrib_name) | BIT(e_attrib_type)
+                            | BIT(e_attrib_dir) | BIT(e_attrib_card)
+                            | BIT(e_attrib_device) | BIT(e_attrib_instances)
+                            | BIT(e_attrib_rate) | BIT(e_attrib_period_size)
+                            | BIT(e_attrib_period_count),
+        .required_attribs = BIT(e_attrib_type),
+        .valid_subelem = BIT(e_elem_stream_ctl)
+                            | BIT(e_elem_enable) | BIT(e_elem_disable)
+                            | BIT(e_elem_usecase),
+        .start_fn = parse_stream_start,
+        .end_fn = parse_stream_end
+        },
+
+    [e_elem_enable] =    {
+        .name = "enable",
+        .valid_attribs = BIT(e_attrib_path),
+        .required_attribs = BIT(e_attrib_path),
+        .valid_subelem = 0,
+        .start_fn = parse_enable_start,
+        .end_fn = NULL
+        },
+
+    [e_elem_disable] =    {
+        .name = "disable",
+        .valid_attribs = BIT(e_attrib_path),
+        .required_attribs = BIT(e_attrib_path),
+        .valid_subelem = 0,
+        .start_fn = parse_disable_start,
+        .end_fn = NULL
+        },
+
+    [e_elem_case] =    {
+        .name = "case",
+        .valid_attribs = BIT(e_attrib_name),
+        .required_attribs = BIT(e_attrib_name),
+        .valid_subelem = BIT(e_elem_ctl),
+        .start_fn = parse_case_start,
+        .end_fn = parse_case_end
+        },
+
+    [e_elem_usecase] =    {
+        .name = "usecase",
+        .valid_attribs = BIT(e_attrib_name),
+        .required_attribs = BIT(e_attrib_name),
+        .valid_subelem = BIT(e_elem_case),
+        .start_fn = parse_usecase_start,
+        .end_fn = parse_usecase_end
+        },
+
+    [e_elem_stream_ctl] =    {
+        .name = "ctl",
+        .valid_attribs = BIT(e_attrib_name) | BIT(e_attrib_function)
+                            | BIT(e_attrib_index),
+        .required_attribs = BIT(e_attrib_name) | BIT(e_attrib_function),
+        .valid_subelem = 0,
+        .start_fn = parse_stream_ctl_start,
+        .end_fn = NULL
+        },
+
+    [e_elem_init] =     {
+        .name = "init",
+        .valid_attribs = 0,
+        .required_attribs = 0,
+        .valid_subelem = BIT(e_elem_ctl),
+        .start_fn = parse_init_start,
+        .end_fn = NULL
+        },
+
+    [e_elem_mixer] =    {
+        .name = "mixer",
+        .valid_attribs = BIT(e_attrib_card),
+        .required_attribs = 0,
+        .valid_subelem = BIT(e_elem_init),
+        .start_fn = parse_mixer_start,
+        .end_fn = NULL
+        },
+
+    [e_elem_audiohal] =    {
+        .name = "audiohal",
+        .valid_attribs = 0,
+        .required_attribs = 0,
+        .valid_subelem = BIT(e_elem_mixer),
+        .start_fn = NULL,
+        .end_fn = NULL
+        }
+};
+
+static const struct parse_attrib attrib_table[e_attrib_count] = {
+    [e_attrib_name] =       {"name"},
+    [e_attrib_val] =        {"val"},
+    [e_attrib_path] =       {"path"},
+    [e_attrib_function] =   {"function"},
+    [e_attrib_type] =       {"type"},
+    [e_attrib_index] =      {"index"},
+    [e_attrib_dir] =        {"dir"},
+    [e_attrib_card] =       {"card"},
+    [e_attrib_device] =     {"device"},
+    [e_attrib_instances] =  {"instances"},
+    [e_attrib_rate] =       {"rate"},
+    [e_attrib_period_size] = {"period_size"},
+    [e_attrib_period_count] = {"period_count"}
+};
+
+static const struct parse_device device_table[] = {
+    {"global",      0}, /* special dummy device for global settings */
+    {"speaker",     AUDIO_DEVICE_OUT_SPEAKER},
+    {"earpiece",    AUDIO_DEVICE_OUT_EARPIECE},
+    {"headset",     AUDIO_DEVICE_OUT_WIRED_HEADSET},
+    {"headset_in",  AUDIO_DEVICE_IN_WIRED_HEADSET},
+    {"headphone",   AUDIO_DEVICE_OUT_WIRED_HEADPHONE},
+    {"sco",         AUDIO_DEVICE_OUT_ALL_SCO},
+    {"sco_in",      AUDIO_DEVICE_IN_ALL_SCO},
+    {"a2dp",        AUDIO_DEVICE_OUT_ALL_A2DP},
+    {"usb",         AUDIO_DEVICE_OUT_ALL_USB},
+    {"mic",         AUDIO_DEVICE_IN_BUILTIN_MIC},
+    {"back mic",    AUDIO_DEVICE_IN_BACK_MIC},
+    {"voice",       AUDIO_DEVICE_IN_VOICE_CALL},
+    {"aux",         AUDIO_DEVICE_IN_AUX_DIGITAL}
+};
+
+static const char *predefined_path_name_table[] = {
+    [e_path_id_off] = "off",
+    [e_path_id_on] = "on"
+};
+
+static int dyn_array_extend(struct dyn_array *array)
+{
+    const uint elem_size = array->elem_size;
+    const uint new_count = array->count + 1;
+    uint max_count = array->max_count;
+    uint old_size, new_size;
+    void *p;
+    uint8_t *pbyte;
+
+    if (new_count > max_count) {
+        if (max_count > 0xFFFF - DYN_ARRAY_GRANULE) {
+            return -ENOMEM;
+        }
+
+        old_size = max_count * elem_size;
+        max_count += DYN_ARRAY_GRANULE;
+        new_size = max_count * elem_size;
+
+        p = realloc(array->data, new_size);
+        if (!p) {
+            return -ENOMEM;
+        }
+
+        pbyte = p;
+        memset(pbyte + old_size, 0, new_size - old_size);
+
+        array->data = p;
+        array->max_count = max_count;
+    }
+
+    array->count = new_count;
+    return 0;
+}
+
+static void dyn_array_fix(struct dyn_array *array)
+{
+    /* Fixes the allocated memory to exactly the required length
+     * This will always be a shrink, discarding granular allocations
+     * that we don't need */
+    const uint size = array->count * array->elem_size;
+    void *p = realloc(array->data, size);
+
+    if (p)
+        {
+        array->data = p;
+        array->max_count = array->count;
+        }
+}
+
+static void dyn_array_free(struct dyn_array *array)
+{
+    free(array->data);
+}
+
+static struct ctl* new_ctl(struct dyn_array *array, struct mixer_ctl *ctl)
+{
+    struct ctl *c;
+
+    if (dyn_array_extend(array) < 0) {
+        return NULL;
+    }
+
+    c = &array->ctls[array->count - 1];
+    c->index = INVALID_CTL_INDEX;
+    c->ctl = ctl;
+    return c;
+}
+
+static void compress_ctl(struct ctl *ctl)
+{
+}
+
+static struct path* new_path(struct dyn_array *array, int id)
+{
+    struct path *path;
+
+    if (dyn_array_extend(array) < 0) {
+        return NULL;
+    }
+
+    path = &array->paths[array->count - 1];
+    path->ctl_array.elem_size = sizeof(struct ctl);
+    path->id = id;
+    return path;
+}
+
+static void compress_path(struct path *path)
+{
+    dyn_array_fix(&path->ctl_array);
+}
+
+static struct scase* new_case(struct dyn_array *array, const char *name)
+{
+    struct scase *sc;
+
+    if (dyn_array_extend(array) < 0) {
+        return NULL;
+    }
+
+    sc = &array->cases[array->count - 1];
+    sc->ctl_array.elem_size = sizeof(struct ctl);
+    sc->name = name;
+    return sc;
+}
+
+static void compress_case(struct scase *sc)
+{
+    dyn_array_fix(&sc->ctl_array);
+}
+
+static struct usecase* new_usecase(struct dyn_array *array, const char *name)
+{
+    struct usecase *puc;
+
+    if (dyn_array_extend(array) < 0) {
+        return NULL;
+    }
+
+    puc = &array->usecases[array->count - 1];
+    puc->case_array.elem_size = sizeof(struct scase);
+    puc->name = name;
+    return puc;
+}
+
+static void compress_usecase(struct usecase *puc)
+{
+    dyn_array_fix(&puc->case_array);
+}
+
+static struct device* new_device(struct dyn_array *array, uint32_t type)
+{
+    struct device *d;
+
+    if (dyn_array_extend(array) < 0) {
+        return NULL;
+    }
+
+    d = &array->devices[array->count - 1];
+    d->path_array.elem_size = sizeof(struct path);
+    d->type = type;
+    return d;
+}
+
+static void compress_device(struct device *d)
+{
+    dyn_array_fix(&d->path_array);
+}
+
+static struct stream* new_stream(struct dyn_array *array, struct config_mgr *cm)
+{
+    struct stream *s;
+
+    if (dyn_array_extend(array) < 0) {
+        return NULL;
+    }
+
+    s = &array->streams[array->count - 1];
+    s->usecase_array.elem_size = sizeof(struct usecase);
+    s->cm = cm;
+    s->enable_path = -1;    /* by default no special path to invoke */
+    s->disable_path = -1;
+    return s;
+}
+
+static void compress_stream(struct stream *s)
+{
+    dyn_array_fix(&s->usecase_array);
+}
+
+static int new_name(struct dyn_array *array, const char* name)
+{
+    int i;
+
+    if (dyn_array_extend(array) < 0) {
+        return -ENOMEM;
+    }
+
+    i = array->count - 1;
+    array->path_names[i] = name;
+    return i;
+}
+
+static struct config_mgr* new_config_mgr()
+{
+    struct config_mgr* mgr = calloc(1, sizeof(struct config_mgr));
+    if (!mgr) {
+        return NULL;
+    }
+    mgr->device_array.elem_size = sizeof(struct device);
+    mgr->stream_array.elem_size = sizeof(struct stream);
+    mgr->named_stream_array.elem_size = sizeof(struct stream);
+    pthread_mutex_init(&mgr->lock, NULL);
+    return mgr;
+}
+
+static void compress_config_mgr(struct config_mgr *mgr)
+{
+    dyn_array_fix(&mgr->device_array);
+    dyn_array_fix(&mgr->stream_array);
+}
+
+static int find_path_name(struct parse_state *state, const char *name)
+{
+    struct dyn_array *array = &state->path_name_array;
+    int i;
+
+    for (i = array->count - 1; i >= 0; --i) {
+        if (0 == strcmp(array->path_names[i], name)) {
+            ALOGV("Existing path '%s' id=%d", name, i);
+            return i;   /* found - return existing index */
+        }
+    }
+    return -EINVAL;
+}
+
+static int add_path_name(struct parse_state *state, const char *name)
+{
+    struct dyn_array *array = &state->path_name_array;
+    int index;
+    const char *s;
+
+    /* Check if already in array */
+    index = find_path_name(state, name);
+    if (index >= 0) {
+        return index;   /* already exists */
+    }
+
+    s = strdup(name);
+    if (s == NULL) {
+        return -ENOMEM;
+    }
+
+    index = new_name(array, s);
+    if (index < 0) {
+        return -ENOMEM;
+    }
+
+    ALOGV("New path '%s' id=%d", name, index);
+    return index;
+}
+
+static void path_names_free(struct parse_state *state)
+{
+    struct dyn_array *array = &state->path_name_array;
+    int i;
+
+    for (i = array->count - 1; i >= 0; --i) {
+        free((void*)array->path_names[i]);
+    }
+    dyn_array_free(array);
+}
+
+static int string_to_uint(uint32_t *result, const char *str)
+{
+    char *endptr;
+    unsigned long int v;
+
+    if (!str) {
+        return -ENOENT;
+    }
+
+    /* return error if not a valid decimal or hex number */
+    v = strtoul(str, &endptr, 0);
+    if ((endptr[0] == '\0') && (endptr != str) && (v <= INT_MAX)) {
+        *result = (uint32_t)v;
+        return 0;
+    } else {
+        ALOGE("'%s' not a valid number", str);
+        return -EINVAL;
+    }
+}
+
+static int attrib_to_uint(uint32_t *result, struct parse_state *state,
+                                enum attrib_index index)
+{
+    const char *str = state->attribs.value[index];
+    return string_to_uint(result, str);
+}
+
+static int make_byte_array(struct parse_state *state, struct ctl *c)
+{
+    char *str = strdup(state->attribs.value[e_attrib_val]);
+    const unsigned int vnum = mixer_ctl_get_num_values(c->ctl);
+    uint8_t *bytes;
+    int count;
+    char *p;
+    uint32_t v;
+    int ret;
+
+    if (!str) {
+        ret = -ENOMEM;
+        goto fail;
+    }
+
+    if (vnum > BYTE_ARRAY_MAX_SIZE) {
+        ALOGE("Byte array control too big(%u)", vnum);
+        return -EINVAL;
+    }
+
+    if (c->index >= vnum) {
+        ALOGE("Control index out of range(%u>%u)", c->index, vnum);
+        ret = -EINVAL;
+        goto fail;
+    }
+
+    /* get number of entries in value string by counting commas */
+    p = strtok(str, ",");
+    for (count = 0; p != NULL; count++) {
+        p = strtok(NULL, ",");
+    }
+
+    if ((c->index + count) > vnum) {
+        ALOGE("Array overflows control (%u+%u > %u)",
+                    c->index, count, vnum);
+        ret = -EINVAL;
+        goto fail;
+    }
+    c->array_count = count;
+
+    bytes = malloc(count);
+    if (!bytes) {
+        ALOGE("Out of memory for control data");
+        ret = -ENOMEM;
+        goto fail;
+    }
+    c->value.data = bytes;
+
+    strcpy(str,state->attribs.value[e_attrib_val]);
+
+    for (p = strtok(str, ","); p != NULL;) {
+        ret = string_to_uint(&v, p);
+        if (ret != 0) {
+            goto fail;
+        }
+        ALOGE_IF(v > 0xFF, "Byte out of range");
+
+        *bytes++ = (uint8_t)v;
+        p = strtok(NULL, ",");
+    }
+
+    free(str);
+    return 0;
+
+fail:
+    free((void *)c->value.data);
+    free(str);
+    return ret;
+}
+
+static const struct parse_device *parse_match_device(const char *name)
+{
+    const struct parse_device *p = &device_table[0];
+    const struct parse_device *p_end =
+            &device_table[ sizeof(device_table) / sizeof(device_table[0]) ];
+
+    for (; p < p_end; ++p) {
+        if (0 == strcmp(name, p->name)) {
+            return p;
+        }
+    }
+
+    return NULL;
+}
+
+static const char *debug_device_to_name(uint32_t device)
+{
+    const struct parse_device *p = &device_table[0];
+    const struct parse_device *p_end =
+            &device_table[ sizeof(device_table) / sizeof(device_table[0]) ];
+
+    /* Assumes device contains a single bitflag plus direction bit */
+
+    for (; p < p_end; ++p) {
+        if (device == p->device) {
+            return p->name;
+        }
+    }
+
+    return "unknown";
+}
+
+
+static int parse_ctl_start(struct parse_state *state)
+{
+    const char *name = state->attribs.value[e_attrib_name];
+    const char *index = state->attribs.value[e_attrib_index];
+    struct dyn_array *array;
+    struct ctl *c;
+    struct mixer_ctl *ctl;
+    enum mixer_ctl_type ctl_type;
+    int ret;
+
+    if (state->current.path) {
+        ALOGV("parse_ctl_start:path ctl");
+        array = &state->current.path->ctl_array;
+    } else {
+        ALOGV("parse_ctl_start:case ctl");
+        array = &state->current.scase->ctl_array;
+    }
+
+    ctl = mixer_get_ctl_by_name(state->cm->mixer, name);
+    if (!ctl) {
+        ALOGE("Control '%s' not found", name);
+        return -EINVAL;
+    }
+
+    c = new_ctl(array, ctl);
+    if (c == NULL) {
+        return -ENOMEM;
+    }
+
+    if (attrib_to_uint(&c->index, state, e_attrib_index) == -EINVAL) {
+        ALOGE("Invalid ctl index");
+        return -EINVAL;
+    }
+
+    ctl_type = mixer_ctl_get_type(ctl);
+    switch(ctl_type)
+        {
+        case MIXER_CTL_TYPE_BYTE:
+            if (c->index == INVALID_CTL_INDEX) {
+                c->index = 0;
+            }
+            ret = make_byte_array(state, c);
+            if (ret != 0) {
+                return ret;
+            }
+            ALOGV("Added ctl '%s' byte array", name);
+            break;
+
+        case MIXER_CTL_TYPE_BOOL:
+        case MIXER_CTL_TYPE_INT:
+            if (attrib_to_uint(&c->value.uinteger, state, e_attrib_val)
+                                                            == -EINVAL) {
+                return -EINVAL;
+            }
+            /* This log statement is just to aid to debugging */
+            ALOGE_IF((ctl_type == MIXER_CTL_TYPE_BOOL)
+                        && (c->value.uinteger > 1),
+                        "WARNING: Illegal value for bool control");
+            ALOGV("Added ctl '%s' value %u", name, c->value.uinteger);
+            break;
+
+        case MIXER_CTL_TYPE_ENUM:
+            c->value.name = strdup(state->attribs.value[e_attrib_val]);
+            if(!c->value.name) {
+                return -ENOMEM;
+            }
+            ALOGV("Added ctl '%s' value '%s'", name, c->value.name);
+            break;
+
+        case MIXER_CTL_TYPE_IEC958:
+        case MIXER_CTL_TYPE_INT64:
+        case MIXER_CTL_TYPE_UNKNOWN:
+        default:
+            ALOGE("Mixer control '%s' has unsupported type", name);
+            return -EINVAL;
+        };
+
+    return 0;
+}
+
+static int parse_init_start(struct parse_state *state)
+{
+    /* The <init> section inside <mixer> is really just a
+     * path that we only use once. We re-use the parsing of
+     * <ctl> entries by creating a temporary path which we
+     * apply at the end of parsing and then discard
+     */
+    state->current.path = &state->init_path;
+
+    ALOGV("Added init path");
+    return 0;
+}
+
+static int parse_path_start(struct parse_state *state)
+{
+    const char *name = state->attribs.value[e_attrib_name];
+    struct device *device = state->current.device;
+    struct dyn_array *array = &device->path_array;
+    struct path *path;
+    int id;
+
+    id = add_path_name(state, name);
+    if (id < 0) {
+        return id;
+    }
+
+    path = new_path(array, id);
+    if (path == NULL) {
+        return -ENOMEM;
+    }
+
+    state->current.path = path;
+
+    ALOGV("Added path '%s' id=%d", name, id);
+    return 0;
+}
+
+static int parse_path_end(struct parse_state *state)
+{
+    /* Free unused memory in the ctl array */
+    compress_path(state->current.path);
+    state->current.path = NULL;
+    return 0;
+}
+
+static int parse_case_start(struct parse_state *state)
+{
+    const char *name = strdup(state->attribs.value[e_attrib_name]);
+    struct usecase *puc = state->current.usecase;
+    struct dyn_array *array = &puc->case_array;
+    struct scase *sc;
+
+    if (!name) {
+        return -ENOMEM;
+    }
+
+    sc = new_case(array, name);
+    if (sc == NULL) {
+        return -ENOMEM;
+    }
+
+    state->current.scase = sc;
+
+    ALOGV("Added case '%s' to '%s'", name, puc->name);
+    return 0;
+}
+
+static int parse_case_end(struct parse_state *state)
+{
+    /* Free unused memory in the ctl array */
+    compress_case(state->current.scase);
+    state->current.scase = NULL;
+    return 0;
+}
+
+static int parse_usecase_start(struct parse_state *state)
+{
+    const char *name = strdup(state->attribs.value[e_attrib_name]);
+    struct dyn_array *array = &state->current.stream->usecase_array;
+    struct usecase *puc;
+
+    if (!name) {
+        return -ENOMEM;
+    }
+
+    puc = new_usecase(array, name);
+    if (puc == NULL) {
+        return -ENOMEM;
+    }
+
+    state->current.usecase = puc;
+
+    ALOGV("Added usecase '%s'", name);
+
+    return 0;
+}
+
+static int parse_usecase_end(struct parse_state *state)
+{
+    /* Free unused memory in the case array */
+    compress_usecase(state->current.usecase);
+    return 0;
+}
+
+static int parse_enable_disable_start(struct parse_state *state, bool is_enable)
+{
+    /* Handling of <enable> and <disable> is almost identical so
+     * they are both handled in this function
+     */
+
+    const char *path_name = state->attribs.value[e_attrib_path];
+    int i;
+
+    i = find_path_name(state,path_name);
+    if (i < 0) {
+        ALOGE("Path '%s' not defined", path_name);
+        return -EINVAL;
+    }
+
+    if (is_enable) {
+        ALOGV("Add enable path '%s' (id=%d)",
+                                state->path_name_array.path_names[i], i);
+        state->current.stream->enable_path = i;
+    } else {
+        ALOGV("Add disable path '%s' (id=%d)",
+                                state->path_name_array.path_names[i], i);
+        state->current.stream->disable_path = i;
+    }
+
+    return 0;
+}
+
+static int parse_enable_start(struct parse_state *state)
+{
+    return parse_enable_disable_start(state, true);
+}
+
+static int parse_disable_start(struct parse_state *state)
+{
+    return parse_enable_disable_start(state, false);
+}
+
+static int parse_stream_ctl_start(struct parse_state *state)
+{
+    /* Parse a <ctl> element within a stream which defines
+     * mixer controls - currently only supports volume controls
+     */
+
+    const char *name = state->attribs.value[e_attrib_name];
+    const char *function = state->attribs.value[e_attrib_function];
+    const char *index = state->attribs.value[e_attrib_index];
+    struct mixer_ctl *ctl;
+    uint idx_val = 0;
+
+    ctl = mixer_get_ctl_by_name(state->cm->mixer, name);
+    if (!ctl) {
+        ALOGE("Control '%s' not found", name);
+        return -EINVAL;
+    }
+
+    if (index != NULL) {
+        if (attrib_to_uint(&idx_val, state, e_attrib_index) == -EINVAL) {
+            return -EINVAL;
+        }
+    }
+
+    if (0 == strcmp(function, "leftvol")) {
+        ALOGE_IF(state->current.stream->controls.volume_left.ctl,
+                                "Left volume control specified again");
+        state->current.stream->controls.volume_left.ctl = ctl;
+        state->current.stream->controls.volume_left.id = idx_val;
+
+    } else if (0 == strcmp(function, "rightvol")) {
+        ALOGE_IF(state->current.stream->controls.volume_right.ctl,
+                                "Right volume control specified again");
+        state->current.stream->controls.volume_right.ctl = ctl;
+        state->current.stream->controls.volume_right.id = idx_val;
+
+    } else {
+        ALOGE("'%s' is not a valid control function", function);
+        return -EINVAL;
+    }
+
+    ALOGV("Added control '%s' function '%s'", name, function);
+
+    return 0;
+}
+
+static int parse_stream_start(struct parse_state *state)
+{
+    const char *type = state->attribs.value[e_attrib_type];
+    const char *dir = state->attribs.value[e_attrib_dir];
+    const char *name = state->attribs.value[e_attrib_name];
+    struct dyn_array *array;
+    bool out;
+    uint32_t card;
+    uint32_t device;
+    uint32_t maxref = INT_MAX;
+    struct stream *s;
+
+    if (name != NULL) {
+        name = strdup(name);
+        if (name == NULL) {
+            return -ENOMEM;
+        }
+    }
+
+    if (name != NULL) {
+        if (find_named_stream(state->cm, name) != NULL) {
+            ALOGE("Stream '%s' already declared", name);
+            return -EINVAL;
+        }
+        array = &state->cm->named_stream_array;
+    } else {
+        array = &state->cm->stream_array;
+    }
+
+    s = new_stream(array, state->cm);
+    if (s == NULL) {
+        return -ENOMEM;
+    }
+
+    if (0 == strcmp(type, "hw")) {
+        if (name == NULL) {
+            ALOGE("Anonymous stream cannot be type hw");
+            return -EINVAL;
+        }
+        s->info.type = e_stream_hardware;
+    } else {
+        if (dir == NULL) {
+            ALOGE("dir tag missing");
+            return -EINVAL;
+        }
+
+        if (0 == strcmp(dir, "out")) {
+            out = true;
+        } else if (0 == strcmp(dir, "in")) {
+            out = false;
+        } else {
+            ALOGE("'%s' is not a valid direction", dir);
+            return -EINVAL;
+        }
+
+        if (0 == strcmp(type, "pcm")) {
+            s->info.type = out ? e_stream_out_pcm : e_stream_in_pcm;
+            card = PCM_CARD_DEFAULT;
+            device = PCM_DEVICE_DEFAULT;
+        } else if (0 == strcmp(type, "compress")) {
+            s->info.type = out ? e_stream_out_compress : e_stream_in_compress;
+            card = COMPRESS_CARD_DEFAULT;
+            device = COMPRESS_DEVICE_DEFAULT;
+        } else {
+            ALOGE("'%s' not a valid stream type", type);
+            return -EINVAL;
+        }
+    }
+
+    if (attrib_to_uint(&card, state, e_attrib_card) == -EINVAL) {
+        return -EINVAL;
+    }
+
+    if (attrib_to_uint(&device, state, e_attrib_device) == -EINVAL) {
+        return -EINVAL;
+    }
+
+    if (attrib_to_uint(&maxref, state, e_attrib_instances) == -EINVAL) {
+        return -EINVAL;
+    }
+
+    if (attrib_to_uint(&s->info.rate, state, e_attrib_rate) == -EINVAL) {
+        return -EINVAL;
+    }
+
+    if (attrib_to_uint(&s->info.period_count, state,
+                        e_attrib_period_count) == -EINVAL) {
+        return -EINVAL;
+    }
+
+    if (attrib_to_uint(&s->info.period_count, state,
+                        e_attrib_period_size) == -EINVAL) {
+        return -EINVAL;
+    }
+
+    s->name = name;
+    s->info.card_number = card;
+    s->info.device_number = device;
+    s->max_ref_count = maxref;
+
+    ALOGV("Added stream %s type=%u card=%u device=%u max_ref=%u",
+                    s->name ? s->name : "",
+                    s->info.type, s->info.card_number, s->info.device_number,
+                    s->max_ref_count );
+
+    state->current.stream = s;
+
+    return 0;
+}
+
+static int parse_stream_end(struct parse_state *state)
+{
+    /* Free unused memory in the ctl array */
+    compress_stream(state->current.stream);
+    return 0;
+}
+
+static int parse_device_start(struct parse_state *state)
+{
+    const char *dev_name = state->attribs.value[e_attrib_name];
+    struct dyn_array *array = &state->cm->device_array;
+    uint32_t device_flag;
+    uint32_t *existing_devices;
+    const struct parse_device *p;
+    struct device* d;
+
+    p = parse_match_device(dev_name);
+
+    if (p == NULL) {
+        ALOGE("'%s' is not a valid device", dev_name);
+        return -EINVAL;
+    }
+
+    device_flag = p->device;
+
+    if (device_flag != 0) {
+        /* not the global device - add it to list of available devices */
+        if (device_flag & AUDIO_DEVICE_BIT_IN) {
+                existing_devices = &state->cm->supported_input_devices;
+        } else {
+                existing_devices = &state->cm->supported_output_devices;
+        }
+
+        if ((device_flag & *existing_devices) == device_flag) {
+            ALOGE("Device '%s' already defined", dev_name);
+            ALOGE("Device = 0x%x extisting_devices = 0x%x", device_flag, *existing_devices);
+            ALOGE("supported_output_devices=0x%x supported_input_devices=0x%x",
+                            state->cm->supported_output_devices,
+                            state->cm->supported_input_devices );
+            return -EINVAL;
+        }
+        *existing_devices |= device_flag;
+    }
+
+    ALOGV("Add device '%s'", dev_name);
+
+    d = new_device(array, device_flag);
+    if (d == NULL) {
+        return -ENOMEM;
+    }
+    state->current.device = d;
+
+    return 0;
+}
+
+static int parse_device_end(struct parse_state *state)
+{
+    /* Free unused memory in the path array */
+    compress_device(state->current.device);
+    return 0;
+}
+
+static int parse_mixer_start(struct parse_state *state)
+{
+    uint32_t card = MIXER_CARD_DEFAULT;
+
+    ALOGV("parse_mixer_start");
+
+    if (attrib_to_uint(&card, state, e_attrib_card) == -EINVAL) {
+        return -EINVAL;
+    }
+
+    ALOGV("Opening mixer card %u", card);
+
+    state->cm->mixer = mixer_open(card);
+
+    if (!state->cm->mixer) {
+        ALOGE("Failed to open mixer card %u", card);
+        return -EINVAL;
+    }
+
+    /* Now we can allow all other root elements but not another <mixer> */
+    state->stack.entry[state->stack.index - 1].valid_subelem =
+                                                  BIT(e_elem_device)
+                                                | BIT(e_elem_stream);
+    return 0;
+}
+
+static int parse_set_error(struct parse_state *state, int error)
+{
+    state->parse_error = error;
+    state->error_line = XML_GetCurrentLineNumber(state->parser);
+    return error;
+}
+
+static int parse_log_error(struct parse_state *state)
+{
+    int err = state->parse_error;
+    int xml_err = XML_GetErrorCode(state->parser);
+
+    if((err < 0) || (xml_err != XML_ERROR_NONE)) {
+        ALOGE_IF(err < 0, "Error in config file at line %d", state->error_line);
+        ALOGE_IF(xml_err != XML_ERROR_NONE,
+                            "Parse error '%s' in config file at line %u",
+                            XML_ErrorString(xml_err),
+                            (uint)XML_GetCurrentLineNumber(state->parser));
+        return -EINVAL;
+    } else {
+        return 0;
+    }
+}
+
+static int extract_attribs(struct parse_state *state, int elem_index)
+{
+    const uint32_t valid_attribs = elem_table[elem_index].valid_attribs;
+    uint32_t required_attribs = elem_table[elem_index].required_attribs;
+    const XML_Char **attribs = state->attribs.all;
+    int i;
+
+    memset(&state->attribs.value, 0, sizeof(state->attribs.value));
+
+    while (attribs[0] != NULL) {
+        for (i = 0; i < e_attrib_count; ++i ) {
+            if ((BIT(i) & valid_attribs) != 0) {
+                if (0 == strcmp(attribs[0], attrib_table[i].name)) {
+                    state->attribs.value[i] = attribs[1];
+                    required_attribs &= ~BIT(i);
+                    break;
+                }
+            }
+        }
+        if (i >= e_attrib_count) {
+            ALOGE("Attribute '%s' not allowed here", attribs[0] );
+            return -EINVAL;
+        }
+
+        attribs += 2;
+    }
+
+    if (required_attribs != 0) {
+        for (i = 0; i < e_attrib_count; ++i ) {
+            if ((required_attribs & BIT(i)) != 0) {
+                ALOGE("Attribute '%s' required", attrib_table[i].name);
+            }
+        }
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static void parse_section_start(void *data, const XML_Char *name,
+                                const XML_Char **attribs)
+{
+    struct parse_state *state = (struct parse_state *)data;
+    int stack_index = state->stack.index;
+    const uint32_t valid_elems =
+                        state->stack.entry[stack_index].valid_subelem;
+    int i;
+
+    if (state->parse_error != 0) {
+        return;
+    }
+
+    ALOGV("parse start <%s>", name );
+
+    /* Find element in list of elements currently valid */
+    for (i = 0; i < e_elem_count; ++i) {
+        if ((BIT(i) & valid_elems) != 0) {
+            if (0 == strcmp(name, elem_table[i].name)) {
+                break;
+            }
+        }
+    }
+
+    if ((i >= e_elem_count) || (stack_index >= MAX_PARSE_DEPTH)) {
+        ALOGE("Element '%s' not allowed here", name);
+        parse_set_error(state, -EINVAL);
+    } else {
+        /* element ok - push onto stack */
+        ++stack_index;
+        state->stack.entry[stack_index].elem_index = i;
+        state->stack.entry[stack_index].valid_subelem
+                                                = elem_table[i].valid_subelem;
+        state->stack.index = stack_index;
+
+        /* Extract attributes and call handler */
+        state->attribs.all = attribs;
+        if (extract_attribs(state, i) != 0) {
+            parse_set_error(state, -EINVAL);
+        } else {
+            if (elem_table[i].start_fn) {
+                parse_set_error(state, (*elem_table[i].start_fn)(state));
+            }
+        }
+    }
+}
+
+static void parse_section_end(void *data, const XML_Char *name)
+{
+    struct parse_state *state = (struct parse_state *)data;
+    const int i = state->stack.entry[state->stack.index].elem_index;
+
+    if (state->parse_error != 0) {
+        return;
+    }
+
+    ALOGV("parse end <%s>", name );
+
+    if (elem_table[i].end_fn) {
+        state->parse_error = (*elem_table[i].end_fn)(state);
+    }
+
+    --state->stack.index;
+}
+
+static int do_parse(struct parse_state *state)
+{
+    bool eof = false;
+    int len;
+    int ret = 0;
+
+    state->parse_error = 0;
+    state->stack.index = 0;
+    /* First element must be <audiohal> */
+    state->stack.entry[0].valid_subelem = BIT(e_elem_audiohal);
+
+    while (!eof && (state->parse_error == 0)) {
+        len = fread(state->read_buf, 1, sizeof(state->read_buf), state->file);
+        if (ferror(state->file)) {
+            ALOGE("I/O error reading config file");
+            ret = -EIO;
+            break;
+        }
+
+        eof = feof(state->file);
+
+        XML_Parse(state->parser, state->read_buf, len, eof);
+        if (parse_log_error(state) < 0) {
+            ret = -EINVAL;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+static int open_config_file(struct parse_state *state)
+{
+    char name[80];
+    char property[PROPERTY_VALUE_MAX];
+
+    property_get("ro.product.device", property, "generic");
+    snprintf(name, sizeof(name), "/system/etc/audio.%s.xml", property);
+
+    ALOGV("Reading configuration from %s\n", name);
+    state->file = fopen(name, "r");
+    if (state->file) {
+        return 0;
+    } else {
+        ALOGE_IF(!state->file, "Failed to open config file %s", name);
+        return -ENOSYS;
+    }
+}
+
+static void cleanup_parser(struct parse_state *state)
+{
+    if (state) {
+        path_names_free(state);
+
+        dyn_array_free(&state->init_path.ctl_array);
+
+        if (state->parser) {
+            XML_ParserFree(state->parser);
+        }
+
+        if (state->file) {
+            fclose(state->file);
+        }
+
+        free(state);
+    }
+}
+
+static int parse_config_file(struct config_mgr *cm)
+{
+    struct parse_state *state;
+    int ret = 0;
+
+    state = calloc(1, sizeof(struct parse_state));
+    if (!state) {
+        return -ENOMEM;
+    }
+    state->cm = cm;
+    state->path_name_array.elem_size = sizeof(const char *);
+    state->init_path.ctl_array.elem_size = sizeof(struct ctl);
+
+    /* "off" and "on" are pre-defined path names */
+    ret = add_path_name(state, predefined_path_name_table[0]);
+    if (ret < 0) {
+        goto fail;
+    }
+    ret = add_path_name(state, predefined_path_name_table[1]);
+    if (ret < 0) {
+        goto fail;
+    }
+
+    ret = open_config_file(state);
+    if (ret == 0) {
+        ret = -ENOMEM;
+        state->parser = XML_ParserCreate(NULL);
+        if (state->parser) {
+            XML_SetUserData(state->parser, state);
+            XML_SetElementHandler(state->parser, parse_section_start, parse_section_end);
+            ret = do_parse(state);
+        }
+    }
+
+    if (ret >= 0) {
+        /* Initialize the mixer by applying the <init> path */
+        /* No need to take mutex during initialization */
+        apply_path_l(&state->init_path);
+    }
+
+fail:
+    cleanup_parser(state);
+    return ret;
+}
+
+/*********************************************************************
+ * Initialization
+ *********************************************************************/
+
+struct config_mgr *init_audio_config()
+{
+    struct stream *streams;
+    int ret;
+
+    struct config_mgr* mgr = new_config_mgr();
+
+    if (0 != parse_config_file(mgr)) {
+        free(mgr);
+        return NULL;
+    }
+
+    /* Free unused memory in the device and stream arrays */
+    compress_config_mgr(mgr);
+
+    return mgr;
+}
+
+static void free_usecases( struct stream *stream )
+{
+    struct usecase *puc = stream->usecase_array.usecases;
+    int uc_count = stream->usecase_array.count;
+    struct scase *pcase;
+    int i;
+
+    for (; uc_count > 0; uc_count--, puc++) {
+        free((void *)puc->name);
+        pcase = puc->case_array.cases;
+        for (i = puc->case_array.count; i > 0; i--, pcase++) {
+            free((void *)pcase->name);
+        }
+    }
+}
+
+void free_audio_config( struct config_mgr *cm )
+{
+    struct dyn_array *path_array, *ctl_array, *stream_array;
+    int dev_idx, path_idx, ctl_idx, stream_idx;
+
+    if (cm) {
+        /* Free all devices */
+        for (dev_idx = cm->device_array.count - 1; dev_idx >= 0; --dev_idx) {
+            /* Free all paths in device */
+            path_array = &cm->device_array.devices[dev_idx].path_array;
+            for (path_idx = path_array->count - 1; path_idx >= 0; --path_idx) {
+                /* Free all ctls in path */
+                ctl_array = &path_array->paths[path_idx].ctl_array;
+                for (ctl_idx = ctl_array->count - 1; ctl_idx >= 0; --ctl_idx) {
+                    if (ctl_array->ctls[ctl_idx].value.name) {
+                        free((void*)ctl_array->ctls[ctl_idx].value.name);
+                        ctl_array->ctls[ctl_idx].value.name = NULL;
+                    }
+                    if (ctl_array->ctls[ctl_idx].value.data) {
+                        free((void*)ctl_array->ctls[ctl_idx].value.data);
+                        ctl_array->ctls[ctl_idx].value.data = NULL;
+                    }
+                }
+                dyn_array_free(ctl_array);
+            }
+
+            dyn_array_free(path_array);
+        }
+        dyn_array_free(&cm->device_array);
+
+        stream_array = &cm->stream_array;
+        for(stream_idx = stream_array->count - 1; stream_idx >= 0; --stream_idx) {
+            free_usecases(&stream_array->streams[stream_idx]);
+        }
+        dyn_array_free(&cm->stream_array);
+
+        stream_array = &cm->named_stream_array;
+        for(stream_idx = stream_array->count - 1; stream_idx >= 0; --stream_idx) {
+            free_usecases(&stream_array->streams[stream_idx]);
+        }
+        dyn_array_free(&cm->stream_array);
+
+        if (cm->mixer) {
+            mixer_close(cm->mixer);
+        }
+        pthread_mutex_destroy(&cm->lock);
+        free(cm);
+    }
+}
+
diff --git a/audio_config.h b/audio_config.h
new file mode 100644
index 0000000..8d26452
--- /dev/null
+++ b/audio_config.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2012-2013 Wolfson Microelectronics plc
+ *
+ * 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.
+ */
+
+#ifndef AUDIO_CONFIG_H
+#define AUDIO_CONFIG_H
+
+#include <stddef.h>
+#include <system/audio.h>
+
+struct mixer_ctl;
+struct config_mgr;
+struct audio_config;
+
+/** Stream type */
+enum stream_type {
+    e_stream_out_pcm,
+    e_stream_in_pcm,
+    e_stream_out_compress,
+    e_stream_in_compress,
+    e_stream_hardware           /* unspecified type routed in hardware */
+};
+
+/** Information about a stream */
+struct hw_stream {
+    enum stream_type    type : 8;
+    uint8_t             card_number;
+    uint8_t             device_number;
+    unsigned int        rate;
+    unsigned int        period_size;
+    unsigned int        period_count;
+};
+
+/** Test whether a stream is an input */
+static inline bool stream_is_input( const struct hw_stream *stream )
+{
+    return (stream->type == e_stream_in_pcm)
+        || (stream->type == e_stream_in_compress);
+}
+
+/** Test whether a stream is PCM */
+static inline bool stream_is_pcm( const struct hw_stream *stream )
+{
+    return (stream->type == e_stream_out_pcm)
+        || (stream->type == e_stream_in_pcm);
+}
+
+/** Test whether a stream is compressed */
+static inline bool stream_is_compressed( const struct hw_stream *stream )
+{
+    return (stream->type == e_stream_out_compress)
+        || (stream->type == e_stream_in_compress);
+}
+
+/** Test whether stream is PCM output */
+static inline bool stream_is_pcm_out( const struct hw_stream *stream )
+{
+return (stream->type == e_stream_out_pcm);
+}
+
+/** Test whether stream is PCM input */
+static inline bool stream_is_pcm_in( const struct hw_stream *stream )
+{
+return (stream->type == e_stream_in_pcm);
+}
+
+/** Test whether stream is compressed output */
+static inline bool stream_is_compressed_out( const struct hw_stream *stream )
+{
+return (stream->type == e_stream_out_compress);
+}
+
+/** Test whether stream is compressed input */
+static inline bool stream_is_compressed_in( const struct hw_stream *stream )
+{
+return (stream->type == e_stream_in_compress);
+}
+
+/** Test whether stream is a hardware link */
+static inline bool stream_is_hardware( const struct hw_stream *stream )
+{
+return (stream->type == e_stream_hardware);
+}
+
+/** Initialize audio config layer */
+struct config_mgr *init_audio_config();
+
+/** Delete audio config layer */
+void free_audio_config( struct config_mgr *cm );
+
+/** Get list of all supported devices */
+uint32_t get_supported_devices( struct config_mgr *cm );
+
+/** Find a suitable stream and return pointer to it */
+const struct hw_stream *get_stream(  struct config_mgr *cm,
+                                        const audio_devices_t devices,
+                                        const audio_output_flags_t flags,
+                                        const struct audio_config *config );
+
+/** Find a named custom stream and return a pointer to it */
+const struct hw_stream *get_named_stream(struct config_mgr *cm,
+                                   const char *name);
+
+/** Test whether a named custom stream is defined */
+bool is_named_stream_defined(struct config_mgr *cm, const char *name);
+
+/** Release stream */
+void release_stream( const struct hw_stream *stream );
+
+/** Get currently connected routes */
+uint32_t get_current_routes( const struct hw_stream *stream );
+
+/** Apply new device routing to a stream */
+void apply_route( const struct hw_stream *stream, uint32_t devices );
+
+/** Get bitmask of devices currently connected to this stream */
+uint32_t get_routed_devices( const struct hw_stream *stream );
+
+/** Adjust routing for all streams that alter with orentiation */
+void rotate_routes( struct config_mgr *cm, int orientation );
+
+/** Apply hardware volume */
+int set_hw_volume( const struct hw_stream *stream, float left, float right);
+
+/** Apply a custom use-case
+ *
+ * @return      0 on success
+ * @return      -ENOSYS if the usecase not declared
+ */
+int apply_use_case( const struct hw_stream* stream,
+                    const char *setting,
+                    const char *case_name);
+#endif  /* ifndef AUDIO_CONFIG_H */
diff --git a/audio_hw.c b/audio_hw.c
index be5cdff..d13649b 100644
--- a/audio_hw.c
+++ b/audio_hw.c
@@ -1,8 +1,9 @@
 /*
- * Copyright (C) 2012 Wolfson Microelectronics plc
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2012-13 Wolfson Microelectronics plc
  *
- * Liberal inspiration drawn from the AOSP code for Toro.
+ * This code is heavily based on AOSP HAL for the asus/grouper
+ *
+ * Copyright (C) 2012 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,8 +18,8 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "tiny_hw"
-#define LOG_NDEBUG 0
+#define LOG_TAG "tinyhal"
+/*#define LOG_NDEBUG 0*/
 
 #include <errno.h>
 #include <pthread.h>
@@ -30,204 +31,278 @@
 #include <cutils/properties.h>
 #include <cutils/str_parms.h>
 
-#include <hardware/hardware.h>
-#include <system/audio.h>
-#include <hardware/audio.h>
+#include <utils/Timers.h>
 
-#include <expat.h>
+#include <hardware/audio.h>
+#include <hardware/hardware.h>
+
+#include <system/audio.h>
 
 #include <tinyalsa/asoundlib.h>
+#include <sound/compress_params.h>
+#include <sound/compress_offload.h>
+#include <tinycompress/tinycompress.h>
+
 #include <audio_utils/resampler.h>
-#include <hardware/audio_effect.h>
 
-struct route_setting
-{
-    char *ctl_name;
-    int intval;
-    char *strval;
+#include "audio_config.h"
+#include "voice_trigger.h"
+
+
+#define OUT_PERIOD_SIZE_DEFAULT 1024
+#define OUT_PERIOD_COUNT_DEFAULT 4
+#define OUT_SAMPLING_RATE_DEFAULT 44100
+#define OUT_CHANNEL_MASK_DEFAULT AUDIO_CHANNEL_OUT_STEREO
+
+#define IN_PERIOD_COUNT_DEFAULT 4
+#define IN_PERIOD_SIZE_DEFAULT 1024
+#define IN_SAMPLING_RATE_DEFAULT 44100
+#define IN_CHANNEL_MASK_DEFAULT AUDIO_CHANNEL_IN_MONO
+
+/* AudioFlinger does not re-read the buffer size after
+ * issuing a routing or input_source change so the
+ * default buffer size must be suitable for both PCM
+ * and compressed inputs
+ */
+#define IN_COMPRESS_BUFFER_SIZE_DEFAULT 8192
+
+/* Maximum time we'll wait for data from a compress_pcm input */
+#define MAX_COMPRESS_PCM_TIMEOUT_MS     2100
+
+struct voice_control_trigger {
+    pthread_t thread;
+    pthread_mutex_t lock;
+    pthread_cond_t waitcv;
+    struct compress *compress;
+    void (*callback)(void *param);
+    void *callback_param;
+    bool own_compress;
+    volatile bool wait;
+    volatile bool terminate;
+    volatile bool triggered;
 };
 
-/* The enable flag when 0 makes the assumption that enums are disabled by
- * "Off" and integers/booleans by 0 */
-static int set_route_by_array(struct mixer *mixer, struct route_setting *route,
-			      unsigned int len)
-{
-    struct mixer_ctl *ctl;
-    unsigned int i, j, ret;
+struct audio_device {
+    struct audio_hw_device hw_device;
 
-    /* Go through the route array and set each value */
-    for (i = 0; i < len; i++) {
-        ctl = mixer_get_ctl_by_name(mixer, route[i].ctl_name);
-        if (!ctl) {
-	    LOGE("Unknown control '%s'\n", route[i].ctl_name);
-            return -EINVAL;
-	}
-
-        if (route[i].strval) {
-	    ret = mixer_ctl_set_enum_by_string(ctl, route[i].strval);
-	    if (ret != 0) {
-		LOGE("Failed to set '%s' to '%s'\n",
-		     route[i].ctl_name, route[i].strval);
-	    } else {
-		LOGV("Set '%s' to '%s'\n",
-		     route[i].ctl_name, route[i].strval);
-	    }
-	    
-        } else {
-            /* This ensures multiple (i.e. stereo) values are set jointly */
-            for (j = 0; j < mixer_ctl_get_num_values(ctl); j++) {
-		ret = mixer_ctl_set_value(ctl, j, route[i].intval);
-		if (ret != 0) {
-		    LOGE("Failed to set '%s'.%d to %d\n",
-			 route[i].ctl_name, j, route[i].intval);
-		} else {
-		    LOGV("Set '%s'.%d to %d\n",
-			 route[i].ctl_name, j, route[i].intval);
-		}
-	    }
-        }
-    }
-
-    return 0;
-}
-
-struct tiny_dev_cfg {
-    int mask;
-
-    struct route_setting *on;
-    unsigned int on_len;
-
-    struct route_setting *off;
-    unsigned int off_len;
-};
-
-struct tiny_audio_device {
-    struct audio_hw_device device;
-    struct mixer *mixer;
-
-    int mode;
-
-    pthread_mutex_t route_lock;
-    struct tiny_dev_cfg *dev_cfgs;
-    int num_dev_cfgs;
-    int active_devices;
-    int devices;
-
+    pthread_mutex_t lock; /* see note below on mutex acquisition order */
+    bool standby;
     bool mic_mute;
+    struct config_mgr *cm;
+    int orientation;
+    bool screen_off;
+
+    struct stream_out_pcm *active_out;
+    struct stream_in_pcm *active_in;
+    struct stream_in_pcm *active_voice_control;
 };
 
-struct tiny_stream_out {
+
+typedef void(*close_fn)(struct audio_stream *);
+
+/* Fields common to all types of output stream */
+struct stream_out_common {
     struct audio_stream_out stream;
 
-    struct tiny_audio_device *adev;
+    close_fn    close;
+    struct audio_device *dev;
+    const struct hw_stream* hw;
 
-    struct pcm_config config;
-    struct pcm *pcm;
+    pthread_mutex_t lock; /* see note below on mutex acquisition order */
+
+    bool standby;
+
+    /* Stream parameters as seen by AudioFlinger
+     * If stream is resampling AudioFlinger buffers before
+     * passing them to hardware, these members refer to the
+     * _input_ data from AudioFlinger
+     */
+    audio_format_t format;
+    uint32_t channel_mask;
+    int channel_count;
+    uint32_t sample_rate;
+    size_t frame_size;
+    uint32_t buffer_size;
+
+    struct {
+        uint32_t    screen_off;
+        uint32_t    screen_on;
+    } latency;
 };
 
-#define MAX_PREPROCESSORS 10
+struct stream_out_pcm {
+    struct stream_out_common common;
 
-struct tiny_stream_in {
+    struct pcm *pcm;
+
+    uint32_t hw_sample_rate;    /* actual sample rate of hardware */
+    int hw_channel_count;  /* actual number of output channels */
+};
+
+/* Fields common to all types of input stream */
+struct stream_in_common {
     struct audio_stream_in stream;
 
-    pthread_mutex_t lock;
+    close_fn    close;
+    struct audio_device *dev;
+    const struct hw_stream* hw;
 
-    struct tiny_audio_device *adev;
+    pthread_mutex_t lock; /* see note below on mutex acquisition order */
 
-    struct pcm_config config;
-    struct pcm *pcm;
+    bool standby;
+
+    /* Stream parameters as seen by AudioFlinger
+     * If stream is resampling AudioFlinger buffers before
+     * passing them to hardware, these members refer to the
+     * _input_ data from AudioFlinger
+     */
+    audio_format_t format;
+    uint32_t channel_mask;
+    int channel_count;
+    uint32_t sample_rate;
+    size_t frame_size;
+    size_t buffer_size;
+
+    int input_source;
+};
+
+struct stream_in_pcm {
+    struct stream_in_common common;
+
+    union {
+        struct pcm *pcm;
+        struct compress *compress;
+    };
+
+    uint32_t hw_sample_rate;    /* actual sample rate of hardware */
+    int hw_channel_count;  /* actual number of input channels */
+    uint32_t period_size;       /* ... of PCM input */
 
     struct resampler_itfe *resampler;
     struct resampler_buffer_provider buf_provider;
     int16_t *buffer;
+    size_t in_buffer_size;
+    int in_buffer_frames;
     size_t frames_in;
-    unsigned int requested_rate;
-    int standby;
-    int source;
-    effect_handle_t preprocessors[MAX_PREPROCESSORS];
-    int num_preprocessors;
-    int16_t *proc_buf;
-    size_t proc_buf_size;
-    size_t proc_frames_in;
     int read_status;
+
+    struct voice_control_trigger *vc_trigger;
 };
 
-/* Must be called with route_lock */
-void select_devices(struct tiny_audio_device *adev)
+enum {
+    ORIENTATION_LANDSCAPE,
+    ORIENTATION_PORTRAIT,
+    ORIENTATION_SQUARE,
+    ORIENTATION_UNDEFINED,
+};
+
+static uint32_t out_get_sample_rate(const struct audio_stream *stream);
+static uint32_t in_get_sample_rate(const struct audio_stream *stream);
+static int get_next_buffer(struct resampler_buffer_provider *buffer_provider,
+                                   struct resampler_buffer* buffer);
+static void release_buffer(struct resampler_buffer_provider *buffer_provider,
+                                  struct resampler_buffer* buffer);
+/*
+ * NOTE: when multiple mutexes have to be acquired, always take the
+ * audio_device mutex first, followed by the stream_in and/or
+ * stream_out mutexes.
+ */
+
+/*********************************************************************
+ * Stream common functions
+ *********************************************************************/
+
+static int common_set_parameters_locked(const struct hw_stream *stream, const char *kvpairs)
 {
-    int i;
+    char *parms = strdup(kvpairs);
+    char *p, *temp;
+    char *pval;
+    char value[32];
+    int ret;
 
-    if (adev->active_devices == adev->devices)
-	return;
+    ALOGV("+common_set_parameters(%p) '%s'", stream, kvpairs);
 
-    LOGV("Changing devices %x => %x\n", adev->active_devices, adev->devices);
+    if (!parms) {
+        return -ENOMEM;
+    }
 
-    /* Turn on new devices first so we don't glitch due to powerdown... */
-    for (i = 0; i < adev->num_dev_cfgs; i++)
-	if ((adev->devices & adev->dev_cfgs[i].mask) &&
-	    !(adev->active_devices & adev->dev_cfgs[i].mask))
-	    set_route_by_array(adev->mixer, adev->dev_cfgs[i].on,
-			       adev->dev_cfgs[i].on_len);
+    /* It's not obvious what we should do if multiple parameters
+     * are given and we only understand some. The action taken
+     * here is to process all that we understand and only return
+     * and error if we don't understand any
+     */
+    ret = -ENOTSUP;
+    p = strtok_r(parms, ";", &temp);
+    while(p) {
+        pval = strchr(p, '=');
+        if (pval && (pval[1] != '\0')) {
+            *pval = '\0';
+            if (apply_use_case(stream, p, pval+1) >= 0) {
+                ret = 0;
+            }
+            *pval = '=';
+        }
+        p = strtok_r(NULL, ";", &temp);
+    }
 
-    /* ...then disable old ones. */
-    for (i = 0; i < adev->num_dev_cfgs; i++)
-	if (!(adev->devices & adev->dev_cfgs[i].mask) &&
-	    (adev->active_devices & adev->dev_cfgs[i].mask))
-	    set_route_by_array(adev->mixer, adev->dev_cfgs[i].off,
-			       adev->dev_cfgs[i].off_len);
-
-    adev->active_devices = adev->devices;
+    return ret;
 }
 
+static int common_get_routing_param(uint32_t *vout, const char *kvpairs)
+{
+    struct str_parms *parms;
+    char value[32];
+    int ret;
+
+    parms = str_parms_create_str(kvpairs);
+
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
+                            value, sizeof(value));
+    if (ret >= 0) {
+        *vout = atoi(value);
+    }
+    str_parms_destroy(parms);
+    return ret;
+}
+
+/*********************************************************************
+ * Output stream common functions
+ *********************************************************************/
+
 static uint32_t out_get_sample_rate(const struct audio_stream *stream)
 {
-    return 44100;
+    struct stream_out_common *out = (struct stream_out_common *)stream;
+    return out->sample_rate;
 }
 
 static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
 {
-    if (rate == out_get_sample_rate(stream))
-	return 0;
-    else
-	return -EINVAL;
+    return -ENOSYS;
 }
 
 static size_t out_get_buffer_size(const struct audio_stream *stream)
 {
-    return 4096;
+    struct stream_out_common *out = (struct stream_out_common *)stream;
+    ALOGV("out_get_buffer_size(%p): %u", stream, out->buffer_size );
+    return out->buffer_size;
 }
 
 static uint32_t out_get_channels(const struct audio_stream *stream)
 {
-    return AUDIO_CHANNEL_OUT_STEREO;
+    struct stream_out_common *out = (struct stream_out_common *)stream;
+    return out->channel_mask;
 }
 
-static int out_get_format(const struct audio_stream *stream)
+static audio_format_t out_get_format(const struct audio_stream *stream)
 {
-    return AUDIO_FORMAT_PCM_16_BIT;
+    struct stream_out_common *out = (struct stream_out_common *)stream;
+    /*ALOGV("out_get_format(%p): 0x%x", stream, out->format );*/
+    return out->format;
 }
 
-static int out_set_format(struct audio_stream *stream, int format)
+static int out_set_format(struct audio_stream *stream, audio_format_t format)
 {
-    return 0;
-}
-
-static int out_standby(struct audio_stream *stream)
-{
-    struct tiny_stream_out *out = (struct tiny_stream_out *)stream;
-    int ret;
-
-    if (out->pcm) {
-	LOGV("out_standby(%p) closing PCM\n", stream);
-	ret = pcm_close(out->pcm);
-	if (ret != 0) {
-	    LOGE("out_standby(%p) failed: %d\n", stream, ret);
-	    return ret;
-	}
-	out->pcm = NULL;
-    }
-
-    return 0;
+    return -ENOSYS;
 }
 
 static int out_dump(const struct audio_stream *stream, int fd)
@@ -237,36 +312,29 @@
 
 static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
 {
-    struct tiny_stream_out *out = (struct tiny_stream_out *)stream;
-    struct tiny_audio_device *adev = out->adev;
-    struct str_parms *parms;
-    char *str;
-    char value[32];
-    int ret, val = 0;
-    bool force_input_standby = false;
+    ALOGV("+out_set_parameters(%p) '%s'", stream, kvpairs);
 
-    parms = str_parms_create_str(kvpairs);
+    struct stream_out_common *out = (struct stream_out_common *)stream;
+    struct audio_device *adev = out->dev;
+    uint32_t v;
+    int ret;
 
-    ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
-			    value, sizeof(value));
+    ret = common_get_routing_param(&v, kvpairs);
+
+    pthread_mutex_lock(&adev->lock);
+
     if (ret >= 0) {
-        val = atoi(value);
-
-	if (val != 0) {
-	    pthread_mutex_lock(&adev->route_lock);
-
-            adev->devices &= ~AUDIO_DEVICE_OUT_ALL;
-            adev->devices |= val;
-            select_devices(adev);
-
-	    pthread_mutex_unlock(&adev->route_lock);
-	} else {
-	    LOGW("output routing with no devices\n");
-	}
+        apply_route(out->hw, v);
     }
 
-    str_parms_destroy(parms);
 
+    if (common_set_parameters_locked(out->hw, kvpairs) >= 0) {
+        ret = 0;
+    }
+
+    pthread_mutex_unlock(&adev->lock);
+
+    ALOGV("-out_set_parameters(%p):%d", out, ret);
     return ret;
 }
 
@@ -277,47 +345,28 @@
 
 static uint32_t out_get_latency(const struct audio_stream_out *stream)
 {
-    return 0;
+    struct stream_out_common *out = (struct stream_out_common *)stream;
+    struct audio_device *adev = out->dev;
+    uint32_t latency;
+
+    pthread_mutex_lock(&adev->lock);
+
+    if (adev->screen_off && !adev->active_in) {
+        latency = out->latency.screen_off;
+    } else {
+        latency = out->latency.screen_on;
+    }
+
+    pthread_mutex_unlock(&adev->lock);
+
+    return latency;
 }
 
 static int out_set_volume(struct audio_stream_out *stream, float left,
                           float right)
 {
-    /* Use the soft volume control for now; AudioFlinger rarely
-     * actually calls down. */
-    return -EINVAL;
-}
-
-static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
-                         size_t bytes)
-{
-    struct tiny_stream_out *out = (struct tiny_stream_out *)stream;
-    int ret;
-
-    if (!out->pcm) {
-	LOGV("out_write(%p) opening PCM\n", stream);
-	out->pcm = pcm_open(0, 0, PCM_OUT | PCM_MMAP, &out->config);
-
-	if (!pcm_is_ready(out->pcm)) {
-	    LOGE("Failed to open output PCM: %s", pcm_get_error(out->pcm));
-	    pcm_close(out->pcm);
-	    return -EBUSY;
-	}
-    }
-
-    ret = pcm_mmap_write(out->pcm, buffer, bytes);
-    if (ret != 0) {
-	LOGE("out_write(%p) failed: %d\n", stream, ret);
-	return ret;
-    }
-
-    return bytes;
-}
-
-static int out_get_render_position(const struct audio_stream_out *stream,
-                                   uint32_t *dsp_frames)
-{
-    return -EINVAL;
+    struct stream_out_common *out = (struct stream_out_common *)stream;
+    return set_hw_volume(out->hw, left, right);
 }
 
 static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
@@ -330,40 +379,294 @@
     return 0;
 }
 
-/** audio_stream_in implementation **/
+static int out_get_next_write_timestamp(const struct audio_stream_out *stream,
+                                        int64_t *timestamp)
+{
+    return -EINVAL;
+}
+
+static void do_close_out_common(struct audio_stream *stream)
+{
+    struct stream_out_common *out = (struct stream_out_common *)stream;
+    release_stream(out->hw);
+    free(stream);
+}
+
+static int do_init_out_common( struct stream_out_common *out,
+                                    const struct audio_config *config )
+{
+    int ret;
+
+    out->standby = true;
+
+    out->stream.common.get_sample_rate = out_get_sample_rate;
+    out->stream.common.set_sample_rate = out_set_sample_rate;
+    out->stream.common.get_buffer_size = out_get_buffer_size;
+    out->stream.common.get_channels = out_get_channels;
+    out->stream.common.get_format = out_get_format;
+    out->stream.common.set_format = out_set_format;
+    out->stream.common.dump = out_dump;
+    out->stream.common.set_parameters = out_set_parameters;
+    out->stream.common.get_parameters = out_get_parameters;
+    out->stream.common.add_audio_effect = out_add_audio_effect;
+    out->stream.common.remove_audio_effect = out_remove_audio_effect;
+    out->stream.get_latency = out_get_latency;
+    out->stream.set_volume = out_set_volume;
+    out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
+
+    /* init requested stream config */
+    out->format = config->format;
+    out->sample_rate = (config->sample_rate == 0)
+                                    ? OUT_SAMPLING_RATE_DEFAULT
+                                    : config->sample_rate;
+    out->channel_mask = (config->channel_mask == 0)
+                                    ? OUT_CHANNEL_MASK_DEFAULT
+                                    : config->channel_mask;
+    out->channel_count = popcount(out->channel_mask);
+
+    /* Default settings */
+    out->frame_size = audio_stream_frame_size(&out->stream.common);
+
+    return 0;
+}
+
+/*********************************************************************
+ * PCM output stream
+ *********************************************************************/
+
+static unsigned int out_pcm_cfg_period_count(struct stream_out_pcm *out)
+{
+    if (out->common.hw->period_count != 0) {
+        return out->common.hw->period_count;
+    } else {
+        return OUT_PERIOD_COUNT_DEFAULT;
+    }
+}
+
+static unsigned int out_pcm_cfg_period_size(struct stream_out_pcm *out)
+{
+    if (out->common.hw->period_size != 0) {
+        return out->common.hw->period_size;
+    } else {
+        return OUT_PERIOD_SIZE_DEFAULT;
+    }
+}
+
+static unsigned int out_pcm_cfg_rate(struct stream_out_pcm *out)
+{
+    if (out->common.hw->rate != 0) {
+        return out->common.hw->rate;
+    } else {
+        return OUT_SAMPLING_RATE_DEFAULT;
+    }
+}
+
+/* must be called with hw device and output stream mutexes locked */
+static void do_out_pcm_standby(struct stream_out_pcm *out)
+{
+    struct audio_device *adev = out->common.dev;
+
+    ALOGV("+do_out_standby(%p)", out);
+
+    if (!out->common.standby) {
+        pcm_close(out->pcm);
+        out->pcm = NULL;
+        adev->active_out = NULL;
+        out->common.standby = true;
+    }
+
+    ALOGV("-do_out_standby(%p)", out);
+}
+
+static void out_pcm_fill_params(struct stream_out_pcm *out,
+                                const struct pcm_config *config )
+{
+    out->hw_sample_rate = config->rate;
+    out->hw_channel_count = config->channels;
+    out->common.buffer_size = pcm_frames_to_bytes(out->pcm,
+                                                pcm_get_buffer_size(out->pcm));
+
+    out->common.latency.screen_on = (config->period_size * config->period_count * 1000)
+                                / config->rate;
+    out->common.latency.screen_off = out->common.latency.screen_on;
+}
+
+/* must be called with hw device and output stream mutexes locked */
+static int start_output_pcm(struct stream_out_pcm *out)
+{
+    struct audio_device *adev = out->common.dev;
+    int ret;
+
+    struct pcm_config config = {
+        .channels = out->common.channel_count,
+        .rate = out_pcm_cfg_rate(out),
+        .period_size = out_pcm_cfg_period_size(out),
+        .period_count = out_pcm_cfg_period_count(out),
+        .format = PCM_FORMAT_S16_LE,
+        .start_threshold = 0,
+        .stop_threshold = 0,
+        .silence_threshold = 0
+    };
+
+    ALOGV("+start_output_stream(%p)", out);
+
+    out->pcm = pcm_open(out->common.hw->card_number,
+                        out->common.hw->device_number,
+                        PCM_OUT,
+                        &config);
+
+    if (out->pcm && !pcm_is_ready(out->pcm)) {
+        ALOGE("pcm_open(out) failed: %s", pcm_get_error(out->pcm));
+        pcm_close(out->pcm);
+        return -ENOMEM;
+    }
+
+    out_pcm_fill_params( out, &config );
+
+    adev->active_out = out;
+
+    ALOGV("-start_output_stream(%p)", out);
+    return 0;
+}
+
+static int out_pcm_standby(struct audio_stream *stream)
+{
+    struct stream_out_pcm *out = (struct stream_out_pcm *)stream;
+
+    pthread_mutex_lock(&out->common.dev->lock);
+    pthread_mutex_lock(&out->common.lock);
+    do_out_pcm_standby(out);
+    pthread_mutex_unlock(&out->common.lock);
+    pthread_mutex_unlock(&out->common.dev->lock);
+
+    return 0;
+}
+
+static ssize_t out_pcm_write(struct audio_stream_out *stream, const void* buffer,
+                         size_t bytes)
+{
+    ALOGV("+out_pcm_write(%p) l=%u", stream, bytes);
+
+    int ret = 0;
+    struct stream_out_pcm *out = (struct stream_out_pcm *)stream;
+    struct audio_device *adev = out->common.dev;
+
+    /* Check that we are routed to something. Android can send routing
+     * commands that tell us to disconnect from everything and in that
+     * state we shouldn't issue any write commands because we can't be
+     * sure that the driver will accept a write to nowhere
+     */
+    if (get_current_routes(out->common.hw) == 0) {
+        ALOGV("-out_pcm_write(%p) 0 (no routes)", stream);
+        return 0;
+    }
+
+    /*
+     * acquiring hw device mutex systematically is useful if a low
+     * priority thread is waiting on the output stream mutex - e.g.
+     * executing out_set_parameters() while holding the hw device
+     * mutex
+     */
+    pthread_mutex_lock(&adev->lock);
+    pthread_mutex_lock(&out->common.lock);
+    if (out->common.standby) {
+        ret = start_output_pcm(out);
+        if (ret != 0) {
+            pthread_mutex_unlock(&adev->lock);
+            goto exit;
+        }
+        out->common.standby = false;
+    }
+    pthread_mutex_unlock(&adev->lock);
+
+    ret = pcm_write(out->pcm, buffer, bytes);
+    if (ret >= 0) {
+        ret = bytes;
+    }
+
+exit:
+    pthread_mutex_unlock(&out->common.lock);
+
+    ALOGV("-out_pcm_write(%p) r=%u", stream, ret);
+
+    return ret;
+}
+
+static int out_pcm_get_render_position(const struct audio_stream_out *stream,
+                                   uint32_t *dsp_frames)
+{
+    return -EINVAL;
+}
+static void do_close_out_pcm(struct audio_stream *stream)
+{
+    out_pcm_standby(stream);
+    do_close_out_common(stream);
+}
+
+static int do_init_out_pcm( struct stream_out_pcm *out,
+                                    const struct audio_config *config )
+{
+    if (config->sample_rate != out_pcm_cfg_rate(out)) {
+        ALOGE("AF requested rate %u not supported", config->sample_rate);
+        return -ENOTSUP;
+    }
+
+    out->common.close = do_close_out_pcm;
+    out->common.stream.common.standby = out_pcm_standby;
+    out->common.stream.write = out_pcm_write;
+    out->common.stream.get_render_position = out_pcm_get_render_position;
+
+    out->common.buffer_size = out_pcm_cfg_period_size(out)
+                               * out_pcm_cfg_period_count(out)
+                               * out->common.frame_size;
+    return 0;
+}
+
+/*********************************************************************
+ * Input stream common functions
+ *********************************************************************/
 static uint32_t in_get_sample_rate(const struct audio_stream *stream)
 {
-    return 8000;
+    const struct stream_in_common *in = (struct stream_in_common *)stream;
+
+    return in->sample_rate;
 }
 
 static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
 {
-    return 0;
+    const struct stream_in_common *in = (struct stream_in_common *)stream;
+
+    if (rate == in->sample_rate) {
+        return 0;
+    } else {
+        return -ENOTSUP;
+    }
+}
+
+static audio_channel_mask_t in_get_channels(const struct audio_stream *stream)
+{
+    const struct stream_in_common *in = (struct stream_in_common *)stream;
+
+    return in->channel_mask;
+}
+
+static audio_format_t in_get_format(const struct audio_stream *stream)
+{
+    const struct stream_in_common *in = (struct stream_in_common *)stream;
+
+    return in->format;
+}
+
+static int in_set_format(struct audio_stream *stream, audio_format_t format)
+{
+    return -ENOSYS;
 }
 
 static size_t in_get_buffer_size(const struct audio_stream *stream)
 {
-    return 320;
-}
-
-static uint32_t in_get_channels(const struct audio_stream *stream)
-{
-    return AUDIO_CHANNEL_IN_MONO;
-}
-
-static int in_get_format(const struct audio_stream *stream)
-{
-    return AUDIO_FORMAT_PCM_16_BIT;
-}
-
-static int in_set_format(struct audio_stream *stream, int format)
-{
-    return 0;
-}
-
-static int in_standby(struct audio_stream *stream)
-{
-    return 0;
+    const struct stream_in_common *in = (struct stream_in_common *)stream;
+    ALOGV("in_get_buffer_size(%p): %u", stream, in->buffer_size );
+    return in->buffer_size;
 }
 
 static int in_dump(const struct audio_stream *stream, int fd)
@@ -387,112 +690,1231 @@
     return 0;
 }
 
-static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
-                       size_t bytes)
-{
-    /* XXX: fake timing for audio input */
-    usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) /
-           in_get_sample_rate(&stream->common));
-    return bytes;
-}
-
 static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
 {
     return 0;
 }
 
-static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+static int in_add_audio_effect(const struct audio_stream *stream,
+                               effect_handle_t effect)
 {
     return 0;
 }
 
-static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+static int in_remove_audio_effect(const struct audio_stream *stream,
+                                  effect_handle_t effect)
 {
     return 0;
 }
 
-static int adev_open_output_stream(struct audio_hw_device *dev,
-                                   uint32_t devices, int *format,
-                                   uint32_t *channels, uint32_t *sample_rate,
-                                   struct audio_stream_out **stream_out)
+static void do_close_in_common(struct audio_stream *stream)
 {
-    struct tiny_audio_device *adev = (struct tiny_audio_device *)dev;
-    struct tiny_stream_out *out;
+    struct stream_in_common *in = (struct stream_in_common *)stream;
+    in->stream.common.standby(stream);
+
+    /* active_voice_control is not cleared by standby so we must
+     * clear it here when stream is closed
+     */
+    if ((struct stream_in_common *)in->dev->active_voice_control == in) {
+        in->dev->active_voice_control = NULL;
+    }
+    release_stream(in->hw);
+    free(stream);
+}
+
+static int do_init_in_common( struct stream_in_common *in,
+                                const struct audio_config *config )
+{
+    in->standby = true;
+
+    in->close = do_close_in_common;
+    in->stream.common.get_sample_rate = in_get_sample_rate;
+    in->stream.common.set_sample_rate = in_set_sample_rate;
+    in->stream.common.get_buffer_size = in_get_buffer_size;
+    in->stream.common.get_channels = in_get_channels;
+    in->stream.common.get_format = in_get_format;
+    in->stream.common.set_format = in_set_format;
+    in->stream.common.dump = in_dump;
+    in->stream.common.set_parameters = in_set_parameters;
+    in->stream.common.get_parameters = in_get_parameters;
+    in->stream.common.add_audio_effect = in_add_audio_effect;
+    in->stream.common.remove_audio_effect = in_remove_audio_effect;
+    in->stream.set_gain = in_set_gain;
+    in->stream.get_input_frames_lost = in_get_input_frames_lost;
+
+    /* init requested stream config */
+    in->format = config->format;
+    in->sample_rate = (config->sample_rate == 0)
+                                    ? IN_SAMPLING_RATE_DEFAULT
+                                    : config->sample_rate;
+    in->channel_mask = (config->channel_mask == 0)
+                                    ? IN_CHANNEL_MASK_DEFAULT
+                                    : config->channel_mask;
+    in->channel_count = popcount(in->channel_mask);
+
+    in->frame_size = audio_stream_frame_size(&in->stream.common);
+
+    return 0;
+}
+
+/*********************************************************************
+ * Voice control triggering
+ * Currently assumes a compressed channel
+ *********************************************************************/
+
+static void* voice_control_trigger_thread(void *param)
+{
+    struct voice_control_trigger *self = param;
+    struct compress *compress;
     int ret;
 
-    out = calloc(1, sizeof(struct tiny_stream_out));
-    if (!out)
-        return -ENOMEM;
+    /* signal that we're alive and ready */
+    pthread_cond_signal(&self->waitcv);
 
-    out->stream.common.get_sample_rate = out_get_sample_rate;
-    out->stream.common.set_sample_rate = out_set_sample_rate;
-    out->stream.common.get_buffer_size = out_get_buffer_size;
-    out->stream.common.get_channels = out_get_channels;
-    out->stream.common.get_format = out_get_format;
-    out->stream.common.set_format = out_set_format;
-    out->stream.common.standby = out_standby;
-    out->stream.common.dump = out_dump;
-    out->stream.common.set_parameters = out_set_parameters;
-    out->stream.common.get_parameters = out_get_parameters;
-    out->stream.common.add_audio_effect = out_add_audio_effect;
-    out->stream.common.remove_audio_effect = out_remove_audio_effect;
-    out->stream.get_latency = out_get_latency;
-    out->stream.set_volume = out_set_volume;
-    out->stream.write = out_write;
-    out->stream.get_render_position = out_get_render_position;
+    while (!self->terminate) {
+        pthread_mutex_lock(&self->lock);
+        if (!self->wait) {
+            pthread_cond_wait(&self->waitcv, &self->lock);
+        }
+        self->wait = false;
+        pthread_mutex_unlock(&self->lock);
 
-    out->adev = adev;
+        if (self->terminate) {
+            break;
+        }
 
-    pthread_mutex_lock(&adev->route_lock);
-    adev->devices &= ~AUDIO_DEVICE_OUT_ALL;
-    adev->devices |= devices;
-    select_devices(adev);
-    pthread_mutex_unlock(&adev->route_lock);
+        /* We must protect against any failure by the main thread to open
+         * the compressed channel
+         */
+        compress = self->compress;
+        if (compress != NULL) {
+            ALOGV("VC wait");
+            ret = compress_wait(compress, -1);
 
-    *channels = out_get_channels(&out->stream.common);
-    *format = out_get_format(&out->stream.common);
-    *sample_rate = out_get_sample_rate(&out->stream.common);
+            if (self->terminate) {
+                break;
+            }
 
-    /* Should query the driver for parameters and compute defaults
-     * from those; should also support configuration from file and
-     * buffer resizing.
+            if (ret == 0) {
+                self->triggered = true;
+                ALOGV("VC trigger %d", ret);
+                (self->callback)(self->callback_param);
+            }
+        }
+    }
+
+    if (self->own_compress && self->compress) {
+        ALOGV("VC trigger thread closes compress");
+        compress_close(self->compress);
+    }
+    free(self);
+
+    ALOGV("VC trigger thread terminates");
+    return NULL;
+}
+
+static void start_voice_control_wait(struct voice_control_trigger *self)
+{
+    ALOGV("start_voice_control_wait");
+
+    pthread_mutex_lock(&self->lock);
+    self->triggered = false;
+    self->wait = true;
+    pthread_cond_signal(&self->waitcv);
+    pthread_mutex_unlock(&self->lock);
+}
+
+static void cancel_voice_control_wait(struct voice_control_trigger *in)
+{
+    ALOGV("cancel_voice_control_wait");
+}
+
+static struct voice_control_trigger* create_voice_control_trigger()
+{
+    static struct voice_control_trigger *t;
+    int ret;
+
+    t = calloc(1, sizeof(struct voice_control_trigger));
+
+    if (t != NULL) {
+        pthread_mutex_init(&t->lock, NULL);
+        pthread_cond_init(&t->waitcv, NULL);
+        pthread_mutex_lock(&t->lock);
+
+        ret = pthread_create(&t->thread, NULL, voice_control_trigger_thread, t);
+        if (ret != 0) {
+            goto fail;
+        }
+
+        /* Wait for thread to initialize */
+        pthread_cond_wait(&t->waitcv, &t->lock);
+        pthread_mutex_unlock(&t->lock);
+    }
+
+    return t;
+
+fail:
+    free(t);
+    return NULL;
+}
+
+static void destroy_voice_control_trigger(struct voice_control_trigger *self,
+                                                bool close_channel)
+{
+    ALOGV("+destroy_voice_control_trigger (close=%d)", close_channel);
+
+    if (self != NULL) {
+        pthread_mutex_lock(&self->lock);
+        if (close_channel && (self->compress != NULL)) {
+            /* Take ownership of the compressed channel and close */
+            /* it as we terminate */
+            /* Must stop the channel to force thread out of the poll */
+            self->own_compress = true;
+            self->terminate = true;
+
+            /* Kick the driver to create an error condition */
+            /* so wait thread will exit the poll */
+
+            if (!is_compress_running(self->compress)) {
+                /* If it's not running, do a dummy start so we */
+                /* can force a stop */
+                compress_start(self->compress);
+            }
+            compress_stop(self->compress);
+        }
+
+        pthread_cond_signal(&self->waitcv);
+        pthread_mutex_unlock(&self->lock);
+
+        /* A struct voice_control_trigger cannot be created without a thread */
+        /* so it is safe to just call pthread_join() here */
+        pthread_join(self->thread, NULL);
+    }
+
+    ALOGV("-destroy_voice_control_trigger");
+}
+
+static void voice_control_callback(void *param)
+{
+    struct stream_in_pcm *in = param;
+
+    if (in->common.standby) {
+        ALOGV("VC trig");
+        send_voice_trigger();
+    } else {
+        ALOGV("VC trig ignored (not in standby)");
+    }
+}
+
+static int init_voice_control(struct stream_in_pcm *in)
+{
+    struct audio_device *adev = in->common.dev;
+    struct voice_control_trigger *t;
+    int ret;
+
+    ALOGV("+init_voice_control");
+    if (!stream_is_compressed(in->common.hw)) {
+        /* We don't support triggering on PCM streams */
+        ret = -EINVAL;
+        goto out;
+    }
+
+    t = create_voice_control_trigger();
+    if (t == NULL) {
+        ret = -ENOMEM;
+        goto out;
+    }
+
+    t->compress = in->compress;
+    t->callback = voice_control_callback;
+    t->callback_param = in;
+    in->vc_trigger = t;
+    ret = 0;
+
+out:
+    ALOGV("-init_voice_control %d", ret);
+    return ret;
+}
+
+/*********************************************************************
+ * PCM input stream via compressed channel
+ *********************************************************************/
+
+/* must be called with hw device and input stream mutexes locked */
+static int do_open_compress_pcm_in(struct stream_in_pcm *in)
+{
+    struct snd_codec codec;
+    struct compress *compress;
+    int ret;
+
+    ALOGV("+do_open_compress_pcm_in");
+
+    memset(&codec, 0, sizeof(codec));
+    codec.id = SND_AUDIOCODEC_PCM;
+    codec.ch_in = in->common.channel_count;
+    codec.sample_rate = compress_get_alsa_rate(in->common.sample_rate);
+    codec.format = SNDRV_PCM_FORMAT_S16_LE;
+
+    /* Fragment and buffer sizes should be configurable or auto-detected
+     * but are currently just hardcoded
      */
-    out->config.channels = 2;
-    out->config.rate = out_get_sample_rate(&out->stream.common);
-    out->config.period_count = 4;
-    out->config.period_size = 1024;
-    out->config.format = PCM_FORMAT_S16_LE;
+    struct compr_config config = {
+        .fragment_size = 4096,
+        .fragments = 1,
+        .codec = &codec
+    };
 
-    LOGV("Opened output stream %p\n", out);
+    compress = compress_open(in->common.hw->card_number,
+                                in->common.hw->device_number,
+                                COMPRESS_OUT,
+                                &config);
 
-    *stream_out = &out->stream;
+    if (!compress || !is_compress_ready(compress)) {
+        ret = errno;
+        ALOGE_IF(compress,"compress_open(in) failed: %s", compress_get_error(compress));
+        ALOGE_IF(!compress,"compress_open(in) failed");
+        compress_close(compress);
+        goto exit;
+    }
+    in->compress = compress;
+    in->common.buffer_size = config.fragment_size * config.fragments * in->common.frame_size;
+    compress_start(in->compress);
+    ret = 0;
+
+exit:
+    ALOGV("-do_open_compress_pcm_in (%d)", ret);
+    return ret;
+}
+
+/* must be called with hw device and input stream mutexes locked */
+static int start_compress_pcm_input_stream(struct stream_in_pcm *in)
+{
+    struct audio_device *adev = in->common.dev;
+    int ret;
+
+    ALOGV("start_compress_pcm_input_stream");
+
+    if (in->common.standby) {
+        /* For voice control we don't need to cancel a pending trigger because
+         * if it's still waiting for a trigger, we won't return any data and
+         * will eventually be put back into standby to continue waiting for
+         * a trigger
+         */
+        if (in->vc_trigger == NULL) {
+            ret = do_open_compress_pcm_in(in);
+            if (ret < 0) {
+                return ret;
+            }
+        }
+
+        adev->active_in = in;
+        in->common.standby = 0;
+    }
+
+    return 0;
+}
+
+static void do_in_compress_pcm_start_vc_trigger(struct stream_in_pcm *in)
+{
+    int ret = do_open_compress_pcm_in(in);
+    if (ret == 0) {
+        in->vc_trigger->compress = in->compress;
+        start_voice_control_wait(in->vc_trigger);
+    }
+}
+
+/* must be called with hw device and input stream mutexes locked */
+static void do_in_compress_pcm_standby(struct stream_in_pcm *in)
+{
+    struct compress *c;
+    int ret;
+
+    ALOGV("+do_in_compress_pcm_standby");
+
+    if (!in->common.standby) {
+        /* Always close, even for a voice control channel.
+         * For voice control we must close the channel after each command
+         */
+        c = in->compress;
+        in->compress = NULL;
+
+        if (in->vc_trigger != NULL) {
+            destroy_voice_control_trigger(in->vc_trigger, true);
+            in->vc_trigger = NULL;
+
+            /* For voice control we must re-open the channel to */
+            /* wait for next trigger */
+            init_voice_control(in);
+            do_in_compress_pcm_start_vc_trigger(in);
+        } else {
+            compress_stop(c);
+            compress_close(c);
+        }
+
+    }
+    in->common.standby = true;
+
+    ALOGV("-do_in_compress_pcm_standby");
+}
+
+static ssize_t do_in_compress_pcm_read(struct audio_stream_in *stream, void* buffer,
+                                        size_t bytes)
+{
+    struct stream_in_pcm *in = (struct stream_in_pcm *)stream;
+    struct audio_device *adev = in->common.dev;
+    size_t frames_rq = bytes / in->common.frame_size;
+    nsecs_t t1;
+    nsecs_t t2;
+    nsecs_t interval;
+    struct timespec ts;
+    int ret = 0;
+
+    ALOGV("+do_in_compress_pcm_read %d", bytes);
+
+    if (get_current_routes(in->common.hw) == 0) {
+        ALOGV("-do_in_compress_pcm_read(%p) 0 (no routes)", stream);
+        return 0;
+    }
+
+    pthread_mutex_lock(&adev->lock);
+    pthread_mutex_lock(&in->common.lock);
+    ret = start_compress_pcm_input_stream(in);
+    pthread_mutex_unlock(&adev->lock);
+
+    if (ret < 0) {
+        goto exit;
+    }
+
+    if (in->vc_trigger != NULL) {
+        if (!in->vc_trigger->triggered) {
+            /* Read without trigger, no data will be available */
+            ALOGV("read without voice trigger - not returning data");
+            ret = 0;
+            goto exit;
+        }
+    }
+
+    t1 = systemTime(SYSTEM_TIME_MONOTONIC);
+    ret = compress_read(in->compress, buffer, bytes);
+    t2 = systemTime(SYSTEM_TIME_MONOTONIC);
+
+    if (ret > 0) {
+        /* The interface between AudioFlinger and AudioRecord cannot cope
+         * with bursty data and will lockup for periods if the data does
+         * not come as a smooth stream. So we must limit the rate that we
+         * deliver PCM buffers to approximately how long the buffer would
+         * have taken to read at its PCM sample rate
+         */
+
+        interval = (1000000000LL * (int64_t)ret) / (in->common.frame_size * in->common.sample_rate);
+        interval -= (interval / 4); /* wait for 75% of PCM time to avoid gaps */
+        t2 -= t1; /* elapsed interval */
+        if (interval > t2) {
+            ts.tv_sec = 0;
+            ts.tv_nsec = interval - t2;
+            nanosleep(&ts, NULL);
+        }
+    }
+
+    /*
+     * Instead of writing zeroes here, we could trust the hardware
+     * to always provide zeroes when muted.
+     */
+    if (ret == 0 && adev->mic_mute) {
+        memset(buffer, 0, bytes);
+        ret = bytes;
+    }
+
+exit:
+    pthread_mutex_unlock(&in->common.lock);
+
+    ALOGV("-do_in_compress_pcm_read (%d)", ret);
+    return ret;
+}
+
+static void do_in_compress_pcm_close(struct stream_in_pcm *in)
+{
+    ALOGV("+do_in_compress_pcm_close");
+
+    if (in->vc_trigger != NULL) {
+        destroy_voice_control_trigger(in->vc_trigger, true);
+    } else if (in->compress != NULL) {
+        compress_stop(in->compress);
+        compress_close(in->compress);
+    }
+
+    ALOGV("-do_in_compress_pcm_close");
+}
+
+/*********************************************************************
+ * PCM input stream
+ *********************************************************************/
+
+static unsigned int in_pcm_cfg_period_count(struct stream_in_pcm *in)
+{
+    if (in->common.hw->period_count != 0) {
+        return in->common.hw->period_count;
+    } else {
+        return IN_PERIOD_COUNT_DEFAULT;
+    }
+}
+
+static unsigned int in_pcm_cfg_period_size(struct stream_in_pcm *in)
+{
+    if (in->common.hw->period_size != 0) {
+        return in->common.hw->period_size;
+    } else {
+        return IN_PERIOD_SIZE_DEFAULT;
+    }
+}
+
+static unsigned int in_pcm_cfg_rate(struct stream_in_pcm *in)
+{
+    if (in->common.hw->rate != 0) {
+        return in->common.hw->rate;
+    } else {
+        return IN_SAMPLING_RATE_DEFAULT;
+    }
+}
+
+/* must be called with hw device and input stream mutexes locked */
+static void do_in_pcm_standby(struct stream_in_pcm *in)
+{
+    struct audio_device *adev = in->common.dev;
+
+    ALOGV("+do_in_pcm_standby");
+
+    if (!in->common.standby) {
+        pcm_close(in->pcm);
+        in->pcm = NULL;
+    }
+    adev->active_in = NULL;
+    if (in->resampler) {
+        release_resampler(in->resampler);
+        in->resampler = NULL;
+    }
+    if (in->buffer) {
+        free(in->buffer);
+        in->buffer = NULL;
+    }
+    in->common.standby = true;
+
+    ALOGV("-do_in_pcm_standby");
+}
+
+static void in_pcm_fill_params(struct stream_in_pcm *in,
+                                const struct pcm_config *config )
+{
+    size_t size;
+
+    in->hw_sample_rate = config->rate;
+    in->hw_channel_count = config->channels;
+    in->period_size = config->period_size;
+
+    /*
+     * take resampling into account and return the closest majoring
+     * multiple of 16 frames, as audioflinger expects audio buffers to
+     * be a multiple of 16 frames
+     */
+    size = (config->period_size * in->common.sample_rate) / config->rate;
+    size = ((size + 15) / 16) * 16;
+    in->common.buffer_size = size * in->common.frame_size;
+
+}
+
+/* must be called with hw device and input stream mutexes locked */
+static int do_open_pcm_input(struct stream_in_pcm *in)
+{
+    int ret;
+
+    struct pcm_config config = {
+        .channels = popcount(IN_CHANNEL_MASK_DEFAULT),
+        .rate = in_pcm_cfg_rate(in),
+        .period_size = in_pcm_cfg_period_size(in),
+        .period_count = in_pcm_cfg_period_count(in),
+        .format = PCM_FORMAT_S16_LE,
+    };
+
+    config.start_threshold = config.period_size * config.period_count;
+
+    ALOGV("+do_open_pcm_input");
+
+    in->pcm = pcm_open(in->common.hw->card_number,
+                       in->common.hw->device_number,
+                       PCM_IN,
+                       &config);
+
+    if (!in->pcm || !pcm_is_ready(in->pcm)) {
+        ALOGE_IF(in->pcm,"pcm_open(in) failed: %s", pcm_get_error(in->pcm));
+        ALOGE_IF(!in->pcm,"pcm_open(in) failed");
+        ret = -ENOMEM;
+        goto fail;
+    }
+
+    in_pcm_fill_params( in, &config );
+
+    ALOGV("input buffer size=0x%x", in->common.buffer_size);
+
+    /*
+     * If the stream rate differs from the PCM rate, we need to
+     * create a resampler.
+     */
+    if (in_get_sample_rate(&in->common.stream.common) != config.rate) {
+        in->in_buffer_size = config.period_size * config.period_count *
+                             config.channels * 2;
+        in->in_buffer_frames = in->in_buffer_size / (config.channels * 2);
+        in->buffer = malloc(in->in_buffer_size);
+
+        if (!in->buffer) {
+            ret = -ENOMEM;
+            goto fail;
+        }
+
+        in->buf_provider.get_next_buffer = get_next_buffer;
+        in->buf_provider.release_buffer = release_buffer;
+
+        ret = create_resampler(config.rate,
+                               in->common.sample_rate,
+                               in->common.channel_count,
+                               RESAMPLER_QUALITY_DEFAULT,
+                               &in->buf_provider,
+                               &in->resampler);
+        if (ret < 0) {
+            goto fail;
+        }
+    }
+    ALOGV("-do_open_pcm_input");
+    return 0;
+
+fail:
+    pcm_close(in->pcm);
+    in->pcm = NULL;
+    ALOGV("-do_open_pcm_input error:%d", ret);
+    return ret;
+}
+
+/* must be called with hw device and input stream mutexes locked */
+static int start_pcm_input_stream(struct stream_in_pcm *in)
+{
+    struct audio_device *adev = in->common.dev;
+    int ret;
+
+    ret = do_open_pcm_input(in);
+
+    if (ret < 0) {
+        return ret;
+    }
+
+    adev->active_in = in;
+    return 0;
+}
+
+static int change_input_locale_locked(struct stream_in_pcm *in, const char *locale)
+{
+    int ret;
+
+    if (in->vc_trigger) {
+        if (!in->common.standby) {
+            ALOGE("attempt to change input locale while active");
+            return -EINVAL;
+        }
+
+        ALOGE("change voice control locale to %s", locale);
+
+        destroy_voice_control_trigger(in->vc_trigger, true);
+
+        /* Execute locale-change use-case */
+        ret = apply_use_case(in->common.hw, "locale", locale);
+        if (ret == -ENOSYS) {
+            /* use-case not implemented.
+             * As we don't support the requested locale switch to default
+             * rather than potentially staying in a totally wrong language
+             */
+             apply_use_case(in->common.hw, "locale", "");
+        }
+
+        /* restart the trigger wait */
+        init_voice_control(in);
+        do_in_compress_pcm_start_vc_trigger(in);
+    }
+
+    return 0;
+}
+
+static int change_input_source_locked(struct stream_in_pcm *in, const char *value,
+                                uint32_t devices, bool *was_changed)
+{
+    struct audio_device *adev = in->common.dev;
+    struct audio_config config;
+    const char *stream_name;
+    const struct hw_stream *hw;
+    bool voice_control = false;
+    const int new_source = atoi(value);
+
+    *was_changed = false;
+
+    if (!in->common.standby) {
+        ALOGE("attempt to change input source while active");
+        return -EINVAL;
+    }
+
+    if (in->common.input_source == new_source) {
+        ALOGV("input source not changed");
+        return 0;
+    }
+
+    /* Special input sources are obtained from the configuration
+     * by opening a named stream
+     */
+    switch (new_source) {
+    case AUDIO_SOURCE_VOICE_RECOGNITION:
+        /* We should verify here that current frame size, sample rate and
+         * channels are compatible
+         */
+
+        stream_name = "voice recognition";
+        voice_control = true;
+        break;
+
+    default:
+        stream_name = NULL;
+        break;
+    }
+
+    if (stream_name) {
+        hw = get_named_stream(in->common.dev->cm, stream_name);
+        ALOGV_IF(hw != NULL, "Changing input source to %s", stream_name);
+    } else {
+        memset(&config, 0, sizeof(config));
+        config.sample_rate = in->common.sample_rate;
+        config.channel_mask = in->common.channel_mask;
+        config.format = in->common.format;
+        hw = get_stream(in->common.dev->cm, devices, 0, &config);
+        ALOGV_IF(hw != NULL, "Changing to default input source for devices 0x%x",
+                        devices);
+    }
+
+    if (hw != NULL) {
+        /* A normal stream will be in standby and therefore device node */
+        /* is closed when we get here. Only in case of a voice control */
+        /* stream will it still be open */
+        if (in->vc_trigger != NULL) {
+            destroy_voice_control_trigger(in->vc_trigger, true);
+        }
+        release_stream(in->common.hw);
+        in->common.hw = hw;
+
+        in->vc_trigger = NULL;
+
+        if (voice_control) {
+            /* Voice control wait will be started when AudioFlinger
+             * puts stream into standby
+             */
+            init_voice_control(in);
+
+            adev->active_voice_control = in;
+        } else if (adev->active_voice_control == in) {
+            adev->active_voice_control = NULL;
+        }
+        in->common.input_source = new_source;
+        *was_changed = true;
+        return 0;
+    } else {
+        ALOGV("Could not open new input stream");
+        return -EINVAL;
+    }
+}
+
+static int get_next_buffer(struct resampler_buffer_provider *buffer_provider,
+                                   struct resampler_buffer* buffer)
+{
+    struct stream_in_pcm *in;
+
+    if (buffer_provider == NULL || buffer == NULL) {
+        return -EINVAL;
+    }
+
+    in = (struct stream_in_pcm *)((char *)buffer_provider -
+                                   offsetof(struct stream_in_pcm, buf_provider));
+
+    if (in->pcm == NULL) {
+        buffer->raw = NULL;
+        buffer->frame_count = 0;
+        in->read_status = -ENODEV;
+        return -ENODEV;
+    }
+
+    if (in->frames_in == 0) {
+        in->read_status = pcm_read(in->pcm,
+                                   (void*)in->buffer,
+                                   in->in_buffer_size);
+        if (in->read_status != 0) {
+            ALOGE("get_next_buffer() pcm_read error %d", errno);
+            buffer->raw = NULL;
+            buffer->frame_count = 0;
+            return in->read_status;
+        }
+        in->frames_in = in->in_buffer_frames;
+        if ((in->common.channel_count == 1) && (in->hw_channel_count == 2)) {
+            unsigned int i;
+
+            /* Discard right channel */
+            for (i = 1; i < in->frames_in; i++) {
+                in->buffer[i] = in->buffer[i * 2];
+            }
+        }
+    }
+
+    buffer->frame_count = (buffer->frame_count > in->frames_in) ?
+                                in->frames_in : buffer->frame_count;
+    buffer->i16 = (int16_t*)in->buffer + ((in->in_buffer_frames - in->frames_in));
+
+    return in->read_status;
+
+}
+
+static void release_buffer(struct resampler_buffer_provider *buffer_provider,
+                                  struct resampler_buffer* buffer)
+{
+    struct stream_in_pcm *in;
+
+    if (buffer_provider == NULL || buffer == NULL)
+        return;
+
+    in = (struct stream_in_pcm *)((char *)buffer_provider -
+                                   offsetof(struct stream_in_pcm, buf_provider));
+
+    in->frames_in -= buffer->frame_count;
+}
+
+/* read_frames() reads frames from kernel driver, down samples to capture rate
+ * if necessary and output the number of frames requested to the buffer specified */
+static ssize_t read_frames(struct stream_in_pcm *in, void *buffer, ssize_t frames)
+{
+    ssize_t frames_wr = 0;
+
+    while (frames_wr < frames) {
+        size_t frames_rd = frames - frames_wr;
+        if (in->resampler != NULL) {
+            in->resampler->resample_from_provider(in->resampler,
+                    (int16_t *)((char *)buffer +
+                            frames_wr * in->common.frame_size),
+                    &frames_rd);
+        } else {
+            struct resampler_buffer buf = {
+                    { raw : NULL, },
+                    frame_count : frames_rd,
+            };
+            get_next_buffer(&in->buf_provider, &buf);
+            if (buf.raw != NULL) {
+                memcpy((char *)buffer +
+                           frames_wr * in->common.frame_size,
+                        buf.raw,
+                        buf.frame_count * in->common.frame_size);
+                frames_rd = buf.frame_count;
+            }
+            release_buffer(&in->buf_provider, &buf);
+        }
+        /* in->read_status is updated by getNextBuffer() also called by
+         * in->resampler->resample_from_provider() */
+        if (in->read_status != 0)
+            return in->read_status;
+
+        frames_wr += frames_rd;
+    }
+    return frames_wr;
+}
+
+static ssize_t do_in_pcm_read(struct audio_stream_in *stream, void* buffer,
+                       size_t bytes)
+{
+    int ret = 0;
+    struct stream_in_pcm *in = (struct stream_in_pcm *)stream;
+    struct audio_device *adev = in->common.dev;
+    size_t frames_rq = bytes / in->common.frame_size;
+
+    ALOGV("+in_pcm_read %d", bytes);
+
+    if (get_current_routes(in->common.hw) == 0) {
+        ALOGV("-in_pcm_read(%p) 0 (no routes)", stream);
+        return 0;
+    }
+
+    /*
+     * acquiring hw device mutex systematically is useful if a low
+     * priority thread is waiting on the input stream mutex - e.g.
+     * executing in_set_parameters() while holding the hw device
+     * mutex
+     */
+    pthread_mutex_lock(&adev->lock);
+    pthread_mutex_lock(&in->common.lock);
+    if (in->common.standby) {
+        ret = start_pcm_input_stream(in);
+        if (ret == 0) {
+            in->common.standby = 0;
+        }
+    }
+    pthread_mutex_unlock(&adev->lock);
+
+    if (ret < 0) {
+        goto exit;
+    }
+
+    if (in->resampler != NULL) {
+        ret = read_frames(in, buffer, frames_rq);
+    } else {
+        ret = pcm_read(in->pcm, buffer, bytes);
+    }
+
+    /*
+     * Instead of writing zeroes here, we could trust the hardware
+     * to always provide zeroes when muted.
+     */
+    if (ret == 0 && adev->mic_mute) {
+        memset(buffer, 0, bytes);
+    }
+
+    if (ret >= 0) {
+        ret = bytes;
+    }
+
+exit:
+    if (ret < 0) {
+        usleep(bytes * 1000000 / in->common.frame_size /
+               in->common.sample_rate);
+    }
+
+    pthread_mutex_unlock(&in->common.lock);
+
+    ALOGV("-in_pcm_read (%d)", ret);
+    return ret;
+}
+
+static int in_pcm_standby(struct audio_stream *stream)
+{
+    struct stream_in_pcm *in = (struct stream_in_pcm *)stream;
+
+    pthread_mutex_lock(&in->common.dev->lock);
+    pthread_mutex_lock(&in->common.lock);
+
+    if (stream_is_compressed_in(in->common.hw)) {
+        do_in_compress_pcm_standby(in);
+    } else {
+        do_in_pcm_standby(in);
+    }
+
+    pthread_mutex_unlock(&in->common.lock);
+    pthread_mutex_unlock(&in->common.dev->lock);
+
+    return 0;
+}
+
+static ssize_t in_pcm_read(struct audio_stream_in *stream, void* buffer,
+                       size_t bytes)
+{
+    struct stream_in_pcm *in = (struct stream_in_pcm *)stream;
+
+    if (stream_is_compressed_in(in->common.hw)) {
+        return do_in_compress_pcm_read(stream, buffer, bytes);
+    } else {
+        return do_in_pcm_read(stream, buffer, bytes);
+    }
+}
+
+static int in_pcm_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+    struct stream_in_pcm *in = (struct stream_in_pcm *)stream;
+    struct audio_device *adev = in->common.dev;
+    struct str_parms *parms;
+    char value[32];
+    uint32_t new_routing = 0;
+    bool routing_changed;
+    uint32_t devices;
+    bool input_was_changed;
+    bool start_vc_trig = false;
+    int ret;
+
+    ALOGV("+in_pcm_set_parameters(%p) '%s'", stream, kvpairs);
+
+    ret = common_get_routing_param(&new_routing, kvpairs);
+    routing_changed = (ret >= 0);
+    parms = str_parms_create_str(kvpairs);
+
+    pthread_mutex_lock(&adev->lock);
+
+    if(str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE,
+                            value, sizeof(value)) >= 0) {
+
+        if (routing_changed) {
+            devices = new_routing;
+        } else {
+            /* Route new stream to same devices as current stream */
+            devices = get_routed_devices(in->common.hw);
+        }
+
+        ret = change_input_source_locked(in, value, devices, &input_was_changed);
+        if (ret < 0) {
+            goto out;
+        }
+
+        /* We must apply any existing routing to the new stream */
+        new_routing = devices;
+        routing_changed = true;
+
+        /* Defer starting the voice control trigger wait until */
+        /* the routing has been set up */
+        if ((in->vc_trigger != NULL) && (input_was_changed)) {
+            start_vc_trig = true;
+        }
+    }
+
+    if (routing_changed) {
+        ALOGV("Apply routing=0x%x to input stream", new_routing);
+        apply_route(in->common.hw, new_routing);
+        ret = 0;
+    }
+
+    if (start_vc_trig) {
+        do_in_compress_pcm_start_vc_trigger(in);
+    }
+
+    common_set_parameters_locked(in->common.hw, kvpairs);
+
+out:
+    pthread_mutex_unlock(&adev->lock);
+    str_parms_destroy(parms);
+
+    ALOGV("-in_pcm_set_parameters(%p):%d", stream, ret);
+    return ret;
+}
+
+static void do_close_in_pcm(struct audio_stream *stream)
+{
+    struct stream_in_pcm *in = (struct stream_in_pcm *)stream;
+
+    if (stream_is_compressed(in->common.hw)) {
+        do_in_compress_pcm_close(in);
+    }
+    do_close_in_common(stream);
+}
+
+static int do_init_in_pcm( struct stream_in_pcm *in,
+                                    struct audio_config *config )
+{
+    in->common.close = do_close_in_pcm;
+    in->common.stream.common.standby = in_pcm_standby;
+    in->common.stream.common.set_parameters = in_pcm_set_parameters;
+    in->common.stream.read = in_pcm_read;
+
+    /* Default settings for stereo capture */
+    in->common.buffer_size = in_pcm_cfg_period_size(in)
+                           * in_pcm_cfg_period_count(in)
+                           * 2;
+
+    if (in->common.buffer_size > IN_COMPRESS_BUFFER_SIZE_DEFAULT) {
+        in->common.buffer_size = IN_COMPRESS_BUFFER_SIZE_DEFAULT;
+    }
+
+    return 0;
+}
+
+/*********************************************************************
+ * Stream open and close
+ *********************************************************************/
+static int adev_open_output_stream(struct audio_hw_device *dev,
+                                   audio_io_handle_t handle,
+                                   audio_devices_t devices,
+                                   audio_output_flags_t flags,
+                                   struct audio_config *config,
+                                   struct audio_stream_out **stream_out)
+{
+    struct audio_device *adev = (struct audio_device *)dev;
+    union {
+        struct stream_out_common *common;
+        struct stream_out_pcm *pcm;
+    } out;
+    int ret;
+
+    ALOGV("+adev_open_output_stream");
+
+    devices &= AUDIO_DEVICE_OUT_ALL;
+    const struct hw_stream *hw = get_stream(adev->cm, devices, flags, config);
+    if (!hw) {
+        ALOGE("No suitable output stream for devices=0x%x flags=0x%x format=0x%x",
+                    devices, flags, config->format );
+        ret = -EINVAL;
+        goto err_fail;
+    }
+
+    out.common = calloc(1, sizeof(struct stream_out_pcm));
+    if (!out.common) {
+        ret = -ENOMEM;
+        goto err_fail;
+    }
+
+    out.common->dev = adev;
+    out.common->hw = hw;
+    ret = do_init_out_common( out.common, config );
+    if (ret < 0) {
+        goto err_open;
+    }
+
+    ret = do_init_out_pcm( out.pcm, config );
+    if (ret < 0) {
+        goto err_open;
+    }
+
+    /* Update config with initial stream settings */
+    config->format = out.common->format;
+    config->channel_mask = out.common->channel_mask;
+    config->sample_rate = out.common->sample_rate;
+
+    *stream_out = &out.common->stream;
+    ALOGV("-adev_open_output_stream=%p", *stream_out);
     return 0;
 
 err_open:
-    free(out);
+    free(out.common);
     *stream_out = NULL;
+err_fail:
+    ALOGV("-adev_open_output_stream (%d)", ret);
     return ret;
 }
 
 static void adev_close_output_stream(struct audio_hw_device *dev,
                                      struct audio_stream_out *stream)
 {
-    struct tiny_stream_out *out = (struct tiny_stream_out *)stream;
-    LOGV("Closing output stream %p\n", stream);
-    if (out->pcm)
-	pcm_close(out->pcm);
-    free(stream);
+    struct stream_out_common *out = (struct stream_out_common *)stream;
+    ALOGV("adev_close_output_stream(%p)", stream);
+    (out->close)(&stream->common);
 }
 
+static int adev_open_input_stream(struct audio_hw_device *dev,
+                                  audio_io_handle_t handle,
+                                  audio_devices_t devices,
+                                  struct audio_config *config,
+                                  struct audio_stream_in **stream_in)
+{
+    struct audio_device *adev = (struct audio_device *)dev;
+    struct stream_in_pcm *in = NULL;
+    const struct hw_stream *hw = NULL;
+    int ret;
+
+    ALOGV("+adev_open_input_stream");
+
+    *stream_in = NULL;
+
+    /* Respond with a request for mono if a different format is given. */
+    if (config->channel_mask != AUDIO_CHANNEL_IN_MONO) {
+        config->channel_mask = AUDIO_CHANNEL_IN_MONO;
+        ret = -EINVAL;
+        goto fail;
+    }
+
+    devices &= AUDIO_DEVICE_IN_ALL;
+    hw = get_stream(adev->cm, devices, 0, config);
+    if (!hw) {
+        ALOGE("No suitable input stream for devices=0x%x format=0x%x",
+                    devices, config->format );
+        ret = -EINVAL;
+        goto fail;
+    }
+
+    in = (struct stream_in_pcm *)calloc(1, sizeof(struct stream_in_pcm));
+    if (!in) {
+        ret = -ENOMEM;
+        goto fail;
+    }
+
+    in->common.dev = adev;
+    in->common.hw = hw;
+    ret = do_init_in_common( &in->common, config );
+    if (ret < 0) {
+        goto fail;
+    }
+    ret = do_init_in_pcm( in, config );
+    if (ret < 0) {
+        goto fail;
+    }
+
+    *stream_in = &in->common.stream;
+    return 0;
+
+fail:
+    free(in);
+    free((void *)hw);
+    ALOGV("-adev_open_input_stream (%d)", ret);
+    return ret;
+}
+
+static void adev_close_input_stream(struct audio_hw_device *dev,
+                                   struct audio_stream_in *stream)
+{
+    struct stream_in_common *in = (struct stream_in_common *)stream;
+    ALOGV("adev_close_input_stream(%p)", stream);
+    (in->close)(&stream->common);
+}
+
+/*********************************************************************
+ * Global API functions
+ *********************************************************************/
 static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
 {
-    return -ENOSYS;
+    struct audio_device *adev = (struct audio_device *)dev;
+    struct str_parms *parms;
+    char *str;
+    char value[32];
+    int ret;
+
+    parms = str_parms_create_str(kvpairs);
+    ret = str_parms_get_str(parms, "orientation", value, sizeof(value));
+    if (ret >= 0) {
+        int orientation;
+
+        if (strcmp(value, "landscape") == 0)
+            orientation = ORIENTATION_LANDSCAPE;
+        else if (strcmp(value, "portrait") == 0)
+            orientation = ORIENTATION_PORTRAIT;
+        else if (strcmp(value, "square") == 0)
+            orientation = ORIENTATION_SQUARE;
+        else
+            orientation = ORIENTATION_UNDEFINED;
+
+        pthread_mutex_lock(&adev->lock);
+        if (orientation != adev->orientation) {
+            adev->orientation = orientation;
+            /* Change routing for any streams that change with orientation */
+            rotate_routes(adev->cm, orientation);
+        }
+        pthread_mutex_unlock(&adev->lock);
+    }
+
+    ret = str_parms_get_str(parms, "screen_state", value, sizeof(value));
+    if (ret >= 0) {
+        if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0)
+            adev->screen_off = false;
+        else
+            adev->screen_off = true;
+    }
+
+    /* locale changes are only relevant to the voice control stream but
+     * Android does not expose the per-stream setParameters() ability to
+     * Java apps so we must handle this through the global setParameters
+     */
+    if(str_parms_get_str(parms, "locale", value, sizeof(value)) >= 0) {
+        if (adev->active_voice_control) {
+            ret = change_input_locale_locked(adev->active_voice_control, value);
+        }
+    }
+
+    str_parms_destroy(parms);
+    return ret;
 }
 
 static char * adev_get_parameters(const struct audio_hw_device *dev,
                                   const char *keys)
 {
-    return NULL;
+    return strdup("");
 }
 
 static int adev_init_check(const struct audio_hw_device *dev)
@@ -510,92 +1932,38 @@
     return -ENOSYS;
 }
 
-static int adev_set_mode(struct audio_hw_device *dev, int mode)
+static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode)
 {
     return 0;
 }
 
 static int adev_set_mic_mute(struct audio_hw_device *dev, bool state)
 {
-    return -ENOSYS;
+    struct audio_device *adev = (struct audio_device *)dev;
+
+    adev->mic_mute = state;
+
+    return 0;
 }
 
 static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state)
 {
-    return -ENOSYS;
+    struct audio_device *adev = (struct audio_device *)dev;
+
+    *state = adev->mic_mute;
+
+    return 0;
 }
 
 static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
-                                         uint32_t sample_rate, int format,
-                                         int channel_count)
+                                         const struct audio_config *config)
 {
-    return 320;
-}
+    size_t s = IN_PERIOD_SIZE_DEFAULT * IN_PERIOD_COUNT_DEFAULT * 2;
+    if (s > IN_COMPRESS_BUFFER_SIZE_DEFAULT) {
+        s = IN_COMPRESS_BUFFER_SIZE_DEFAULT;
+    }
 
-static int adev_open_input_stream(struct audio_hw_device *dev, uint32_t devices,
-                                  int *format, uint32_t *channels,
-                                  uint32_t *sample_rate,
-                                  audio_in_acoustics_t acoustics,
-                                  struct audio_stream_in **stream_in)
-{
-    struct tiny_audio_device *adev = (struct tiny_audio_device *)dev;
-    struct tiny_stream_in *in;
-    int ret;
-    int channel_count = popcount(*channels);
-
-    in = calloc(1, sizeof(struct tiny_stream_in));
-    if (!in)
-        return -ENOMEM;
-
-    pthread_mutex_init(&in->lock, NULL);
-    in->adev = adev;
-
-    in->stream.common.get_sample_rate = in_get_sample_rate;
-    in->stream.common.set_sample_rate = in_set_sample_rate;
-    in->stream.common.get_buffer_size = in_get_buffer_size;
-    in->stream.common.get_channels = in_get_channels;
-    in->stream.common.get_format = in_get_format;
-    in->stream.common.set_format = in_set_format;
-    in->stream.common.standby = in_standby;
-    in->stream.common.dump = in_dump;
-    in->stream.common.set_parameters = in_set_parameters;
-    in->stream.common.get_parameters = in_get_parameters;
-    in->stream.common.add_audio_effect = in_add_audio_effect;
-    in->stream.common.remove_audio_effect = in_remove_audio_effect;
-    in->stream.set_gain = in_set_gain;
-    in->stream.read = in_read;
-    in->stream.get_input_frames_lost = in_get_input_frames_lost;
-
-    pthread_mutex_lock(&adev->route_lock);
-    adev->devices &= ~AUDIO_DEVICE_IN_ALL;
-    adev->devices |= devices;
-    select_devices(adev);
-    pthread_mutex_unlock(&adev->route_lock);
-
-    in->config.channels = 2;
-    in->config.rate = 44100;
-    in->config.period_count = 4;
-    in->config.period_size = 320;
-    in->config.format = PCM_FORMAT_S16_LE;
-
-    *stream_in = &in->stream;
-    return 0;
-
-err_open:
-    free(in);
-    *stream_in = NULL;
-    return ret;
-}
-
-static void adev_close_input_stream(struct audio_hw_device *dev,
-                                   struct audio_stream_in *stream)
-{
-    struct tiny_stream_in *in = (struct tiny_stream_in *)stream;
-
-    if (in->pcm)
-	pcm_close(in->pcm);
-    free(in);
-    return;
+    return s;
 }
 
 static int adev_dump(const audio_hw_device_t *device, int fd)
@@ -605,306 +1973,72 @@
 
 static int adev_close(hw_device_t *device)
 {
+    struct audio_device *adev = (struct audio_device *)device;
+
+    free_audio_config(adev->cm);
+
     free(device);
     return 0;
 }
 
-static uint32_t adev_get_supported_devices(const struct audio_hw_device *dev)
-{
-    struct tiny_audio_device *adev = (struct tiny_audio_device *)dev;
-    uint32_t supported = 0;
-    int i;
-
-    for (i = 0; i < adev->num_dev_cfgs; i++)
-	supported |= adev->dev_cfgs[i].mask;
-
-    return supported;
-}
-
-struct config_parse_state {
-    struct tiny_audio_device *adev;
-    struct tiny_dev_cfg *dev;
-    bool on;
-
-    struct route_setting *path;
-    unsigned int path_len;
-};
-
-static const struct {
-    int mask;
-    const char *name;
-} dev_names[] = {
-    { AUDIO_DEVICE_OUT_SPEAKER, "speaker" },
-    { AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
-      "headphone" },
-    { AUDIO_DEVICE_OUT_EARPIECE, "earpiece" },
-    { AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET, "analog-dock" },
-    { AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET, "digital-dock" },
-
-    { AUDIO_DEVICE_IN_COMMUNICATION, "comms" },
-    { AUDIO_DEVICE_IN_AMBIENT, "ambient" },
-    { AUDIO_DEVICE_IN_BUILTIN_MIC, "builtin-mic" },
-    { AUDIO_DEVICE_IN_WIRED_HEADSET, "headset" },
-    { AUDIO_DEVICE_IN_AUX_DIGITAL, "digital" },
-    { AUDIO_DEVICE_IN_BACK_MIC, "back-mic" },
-};
-
-static void adev_config_start(void *data, const XML_Char *elem,
-			      const XML_Char **attr)
-{
-    struct config_parse_state *s = data;
-    struct tiny_dev_cfg *dev_cfg;
-    const XML_Char *name = NULL;
-    const XML_Char *val = NULL;
-    unsigned int i, j;
-
-    for (i = 0; attr[i]; i += 2) {
-	if (strcmp(attr[i], "name") == 0)
-	    name = attr[i + 1];
-
-	if (strcmp(attr[i], "val") == 0)
-	    val = attr[i + 1];
-    }
-
-    if (strcmp(elem, "device") == 0) {
-	if (!name) {
-	    LOGE("Unnamed device\n");
-	    return;
-	}
-
-	for (i = 0; i < sizeof(dev_names) / sizeof(dev_names[0]); i++) {
-	    if (strcmp(dev_names[i].name, name) == 0) {
-		LOGI("Allocating device %s\n", name);
-		dev_cfg = realloc(s->adev->dev_cfgs,
-				  (s->adev->num_dev_cfgs + 1)
-				  * sizeof(*dev_cfg));
-		if (!dev_cfg) {
-		    LOGE("Unable to allocate dev_cfg\n");
-		    return;
-		}
-
-		s->dev = &dev_cfg[s->adev->num_dev_cfgs];
-		memset(s->dev, 0, sizeof(*s->dev));
-		s->dev->mask = dev_names[i].mask;
-
-		s->adev->dev_cfgs = dev_cfg;
-		s->adev->num_dev_cfgs++;
-	    }
-	}
-
-    } else if (strcmp(elem, "path") == 0) {
-	if (s->path_len)
-	    LOGW("Nested paths\n");
-
-	/* If this a path for a device it must have a role */
-	if (s->dev) {
-	    /* Need to refactor a bit... */
-	    if (strcmp(name, "on") == 0) {
-		s->on = true;
-	    } else if (strcmp(name, "off") == 0) {
-		s->on = false;
-	    } else {
-		LOGW("Unknown path name %s\n", name);
-	    }
-	}
-
-    } else if (strcmp(elem, "ctl") == 0) {
-	struct route_setting *r;
-
-	if (!name) {
-	    LOGE("Unnamed control\n");
-	    return;
-	}
-
-	if (!val) {
-	    LOGE("No value specified for %s\n", name);
-	    return;
-	}
-
-	LOGV("Parsing control %s => %s\n", name, val);
-
-	r = realloc(s->path, sizeof(*r) * (s->path_len + 1));
-	if (!r) {
-	    LOGE("Out of memory handling %s => %s\n", name, val);
-	    return;
-	}
-
-	r[s->path_len].ctl_name = strdup(name);
-	r[s->path_len].strval = NULL;
-
-	/* This can be fooled but it'll do */
-	r[s->path_len].intval = atoi(val);
-	if (!r[s->path_len].intval && strcmp(val, "0") != 0)
-	    r[s->path_len].strval = strdup(val);
-
-	s->path = r;
-	s->path_len++;
-    }
-}
-
-static void adev_config_end(void *data, const XML_Char *name)
-{
-    struct config_parse_state *s = data;
-    unsigned int i;
-
-    if (strcmp(name, "path") == 0) {
-	if (!s->path_len)
-	    LOGW("Empty path\n");
-
-	if (!s->dev) {
-	    LOGV("Applying %d element default route\n", s->path_len);
-
-	    set_route_by_array(s->adev->mixer, s->path, s->path_len);
-
-	    for (i = 0; i < s->path_len; i++) {
-		free(s->path[i].ctl_name);
-		free(s->path[i].strval);
-	    }
-
-	    free(s->path);
-
-	    /* Refactor! */
-	} else if (s->on) {
-	    LOGV("%d element on sequence\n", s->path_len);
-	    s->dev->on = s->path;
-	    s->dev->on_len = s->path_len;
-
-	} else {
-	    LOGV("%d element off sequence\n", s->path_len);
-
-	    /* Apply it, we'll reenable anything that's wanted later */
-	    set_route_by_array(s->adev->mixer, s->path, s->path_len);
-
-	    s->dev->off = s->path;
-	    s->dev->off_len = s->path_len;
-	}
-
-	s->path_len = 0;
-	s->path = NULL;
-
-    } else if (strcmp(name, "device") == 0) {
-	s->dev = NULL;
-    }
-}
-
-static int adev_config_parse(struct tiny_audio_device *adev)
-{
-    struct config_parse_state s;
-    FILE *f;
-    XML_Parser p;
-    char property[PROPERTY_VALUE_MAX];
-    char file[80];
-    int ret = 0;
-    bool eof = false;
-    int len;
-
-    property_get("ro.product.device", property, "tiny_hw");
-    snprintf(file, sizeof(file), "/system/etc/sound/%s", property);
-
-    LOGV("Reading configuration from %s\n", file);
-    f = fopen(file, "r");
-    if (!f) {
-	LOGE("Failed to open %s\n", file);
-	return -ENODEV;
-    }
-
-    p = XML_ParserCreate(NULL);
-    if (!p) {
-	LOGE("Failed to create XML parser\n");
-	ret = -ENOMEM;
-	goto out;
-    }
-
-    memset(&s, 0, sizeof(s));
-    s.adev = adev;
-    XML_SetUserData(p, &s);
-
-    XML_SetElementHandler(p, adev_config_start, adev_config_end);
-
-    while (!eof) {
-	len = fread(file, 1, sizeof(file), f);
-	if (ferror(f)) {
-	    LOGE("I/O error reading config\n");
-	    ret = -EIO;
-	    goto out_parser;
-	}
-	eof = feof(f);
-
-	if (XML_Parse(p, file, len, eof) == XML_STATUS_ERROR) {
-	    LOGE("Parse error at line %u:\n%s\n",
-		 (unsigned int)XML_GetCurrentLineNumber(p),
-		 XML_ErrorString(XML_GetErrorCode(p)));
-	    ret = -EINVAL;
-	    goto out_parser;
-	}
-    }
-
- out_parser:
-    XML_ParserFree(p);
- out:
-    fclose(f);
-
-    return ret;
-}
-
 static int adev_open(const hw_module_t* module, const char* name,
                      hw_device_t** device)
 {
-    struct tiny_audio_device *adev;
+    struct audio_device *adev;
     int ret;
 
     if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
         return -EINVAL;
 
-    adev = calloc(1, sizeof(struct tiny_audio_device));
+    adev = calloc(1, sizeof(struct audio_device));
     if (!adev)
         return -ENOMEM;
 
-    adev->device.common.tag = HARDWARE_DEVICE_TAG;
-    adev->device.common.version = 0;
-    adev->device.common.module = (struct hw_module_t *) module;
-    adev->device.common.close = adev_close;
+    adev->hw_device.common.tag = HARDWARE_DEVICE_TAG;
+    adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
+    adev->hw_device.common.module = (struct hw_module_t *) module;
+    adev->hw_device.common.close = adev_close;
 
-    adev->device.get_supported_devices = adev_get_supported_devices;
-    adev->device.init_check = adev_init_check;
-    adev->device.set_voice_volume = adev_set_voice_volume;
-    adev->device.set_master_volume = adev_set_master_volume;
-    adev->device.set_mode = adev_set_mode;
-    adev->device.set_mic_mute = adev_set_mic_mute;
-    adev->device.get_mic_mute = adev_get_mic_mute;
-    adev->device.set_parameters = adev_set_parameters;
-    adev->device.get_parameters = adev_get_parameters;
-    adev->device.get_input_buffer_size = adev_get_input_buffer_size;
-    adev->device.open_output_stream = adev_open_output_stream;
-    adev->device.close_output_stream = adev_close_output_stream;
-    adev->device.open_input_stream = adev_open_input_stream;
-    adev->device.close_input_stream = adev_close_input_stream;
-    adev->device.dump = adev_dump;
+    adev->hw_device.init_check = adev_init_check;
+    adev->hw_device.set_voice_volume = adev_set_voice_volume;
+    adev->hw_device.set_master_volume = adev_set_master_volume;
+    adev->hw_device.set_mode = adev_set_mode;
+    adev->hw_device.set_mic_mute = adev_set_mic_mute;
+    adev->hw_device.get_mic_mute = adev_get_mic_mute;
+    adev->hw_device.set_parameters = adev_set_parameters;
+    adev->hw_device.get_parameters = adev_get_parameters;
+    adev->hw_device.get_input_buffer_size = adev_get_input_buffer_size;
+    adev->hw_device.open_output_stream = adev_open_output_stream;
+    adev->hw_device.close_output_stream = adev_close_output_stream;
+    adev->hw_device.open_input_stream = adev_open_input_stream;
+    adev->hw_device.close_input_stream = adev_close_input_stream;
+    adev->hw_device.dump = adev_dump;
 
-    adev->mixer = mixer_open(0);
-    if (!adev->mixer) {
-	LOGE("Failed to open mixer 0\n");
-	goto err;
+    adev->cm = init_audio_config();
+    if (!adev->cm) {
+        ret = -EINVAL;
+        goto fail;
     }
-    
-    ret = adev_config_parse(adev);
-    if (ret != 0)
-	goto err_mixer;
 
-    /* Bootstrap routing */
-    pthread_mutex_init(&adev->route_lock, NULL);
-    adev->mode = AUDIO_MODE_NORMAL;
-    adev->devices = AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_IN_BUILTIN_MIC;
-    select_devices(adev);
+    if (is_named_stream_defined(adev->cm, "voice recognition")) {
+        ret = init_voice_trigger_service();
+        if (ret != 0) {
+            goto fail;
+        }
+    }
 
-    *device = &adev->device.common;
+    adev->orientation = ORIENTATION_UNDEFINED;
 
+    *device = &adev->hw_device.common;
     return 0;
 
-err_mixer:
-    mixer_close(adev->mixer);
-err:
-    return -EINVAL;
+fail:
+    if (adev->cm) {
+        /*free_audio_config(adev->cm);*/ /* Currently broken */
+    }
+
+    free(adev);
+    return ret;
 }
 
 static struct hw_module_methods_t hal_module_methods = {
@@ -914,11 +2048,11 @@
 struct audio_module HAL_MODULE_INFO_SYM = {
     .common = {
         .tag = HARDWARE_MODULE_TAG,
-        .version_major = 1,
-        .version_minor = 0,
+        .module_api_version = AUDIO_MODULE_API_VERSION_0_1,
+        .hal_api_version = HARDWARE_HAL_API_VERSION,
         .id = AUDIO_HARDWARE_MODULE_ID,
         .name = "TinyHAL",
-        .author = "Mark Brown <broonie@opensource.wolfsonmicro.com>",
+        .author = "Richard Fitzgerald <rf@opensource.wolfsonmicro.com>",
         .methods = &hal_module_methods,
     },
 };
diff --git a/voice_trigger.cpp b/voice_trigger.cpp
new file mode 100644
index 0000000..a33781e
--- /dev/null
+++ b/voice_trigger.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2013 Wolfson Microelectronics plc
+ *
+ * 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.
+ */
+
+#define LOG_TAG "tinyhal-vctrig"
+/*#define LOG_NDEBUG 0*/
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+#include <cutils/log.h>
+#include <sysutils/FrameworkListener.h>
+#include <sysutils/FrameworkCommand.h>
+#include <utils/Mutex.h>
+#include <utils/Condition.h>
+
+#include "voice_trigger.h"
+
+static CVoiceTriggerService* TriggerService;
+
+CVoiceTriggerCommand::CVoiceTriggerCommand()
+    : FrameworkCommand("wait")
+{
+}
+
+CVoiceTriggerCommand::~CVoiceTriggerCommand()
+{
+}
+
+int CVoiceTriggerCommand::runCommand(SocketClient *c, int argc, char **argv)
+{
+    return 0;
+}
+
+CVoiceTriggerService::CVoiceTriggerService()
+    : FrameworkListener("voice-trigger", true)
+{
+    registerCmd(&mTriggerCommand);
+}
+
+CVoiceTriggerService::~CVoiceTriggerService()
+{
+}
+
+
+// As the rest of TinyHAL is currently written in C we provide a
+// C interface to the voice trigger service
+
+extern "C"
+void send_voice_trigger()
+{
+    if (TriggerService != NULL) {
+        ALOGV("trigger");
+        TriggerService->sendBroadcast(0, "trig", 0);
+    }
+}
+
+extern "C"
+int init_voice_trigger_service()
+{
+    ALOGV("init_voice_trigger_service");
+
+    CVoiceTriggerService *svc = new CVoiceTriggerService;
+    if (svc->startListener() != 0) {
+        int ret = errno;
+        delete svc;
+        return ret;
+    }
+
+    TriggerService = svc;
+    return 0;
+}
+
+
+
+
diff --git a/voice_trigger.h b/voice_trigger.h
new file mode 100644
index 0000000..43507b3
--- /dev/null
+++ b/voice_trigger.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012-13 Wolfson Microelectronics plc
+ *
+ * This code is heavily based on AOSP HAL for the asus/grouper
+ *
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef VOICE_TRIGGER_H
+#define VOICE_TRIGGER_H
+
+#if defined(__cplusplus)
+#include <sysutils/FrameworkListener.h>
+#include <sysutils/FrameworkCommand.h>
+#include <utils/Mutex.h>
+#include <utils/Condition.h>
+
+class CVoiceTriggerCommand : public FrameworkCommand
+{
+public:
+    CVoiceTriggerCommand();
+    virtual ~CVoiceTriggerCommand();
+
+private:
+    int runCommand(SocketClient *c, int argc, char **argv);
+};
+
+class CVoiceTriggerService : public FrameworkListener
+{
+public:
+    CVoiceTriggerService();
+    virtual ~CVoiceTriggerService();
+
+public:
+    CVoiceTriggerCommand mTriggerCommand;
+};
+
+extern "C" {
+#endif
+
+void send_voice_trigger();
+int init_voice_trigger_service();
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif