Split out new device wingray.

Change-Id: I19c27ff5f3961e9adb6ca25a6f458920fdafa932
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..97b90e7
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,15 @@
+# Copyright (C) 2010 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 := $(my-dir)
diff --git a/AndroidBoard.mk b/AndroidBoard.mk
new file mode 100644
index 0000000..0d81689
--- /dev/null
+++ b/AndroidBoard.mk
@@ -0,0 +1,49 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+ifeq ($(TARGET_PREBUILT_KERNEL),)
+TARGET_PREBUILT_KERNEL := $(LOCAL_PATH)/kernel
+endif
+
+file := $(INSTALLED_KERNEL_TARGET)
+ALL_PREBUILT += $(file)
+$(file): $(TARGET_PREBUILT_KERNEL) | $(ACP)
+	$(transform-prebuilt-to-target)
+
+include $(CLEAR_VARS)
+
+target_hw_init_stingray_rc_file := $(TARGET_ROOT_OUT)/init.stingray.rc
+target_hw_init_olympus_rc_file := $(TARGET_ROOT_OUT)/init.olympus.rc
+target_hw_ueventd_stingray_rc_file := $(TARGET_ROOT_OUT)/ueventd.stingray.rc
+target_hw_ueventd_olympus_rc_file := $(TARGET_ROOT_OUT)/ueventd.olympus.rc
+
+$(target_hw_init_stingray_rc_file) : $(LOCAL_PATH)/init.stingray.rc | $(ACP)
+	$(transform-prebuilt-to-target)
+$(target_hw_init_olympus_rc_file) : $(LOCAL_PATH)/init.stingray.rc | $(ACP)
+	$(transform-prebuilt-to-target)
+$(target_hw_ueventd_stingray_rc_file) : $(LOCAL_PATH)/ueventd.stingray.rc | $(ACP)
+	$(transform-prebuilt-to-target)
+$(target_hw_ueventd_olympus_rc_file) : $(LOCAL_PATH)/ueventd.stingray.rc | $(ACP)
+	$(transform-prebuilt-to-target)
+
+ALL_PREBUILT += $(target_hw_init_stingray_rc_file) \
+                $(target_hw_init_olympus_rc_file) \
+                $(target_hw_ueventd_stingray_rc_file) \
+                $(target_hw_ueventd_olympus_rc_file)
+
+include $(CLEAR_VARS)
+
+COMMON_DIR := vendor/nvidia/common/
+
+ifeq ($(wildcard $(COMMON_DIR)/TegraBoard.mk),$(COMMON_DIR)/TegraBoard.mk)
+include $(COMMON_DIR)/TegraBoard.mk
+endif
+
+subdir_makefiles:= \
+	$(LOCAL_PATH)/ril/Android.mk \
+	$(LOCAL_PATH)/libaudio/Android.mk \
+	$(LOCAL_PATH)/taudio/Android.mk
+
+include $(subdir_makefiles)
+
+-include vendor/moto/stingray/AndroidBoardVendor.mk
diff --git a/BoardConfig.mk b/BoardConfig.mk
new file mode 100644
index 0000000..3acc0ab
--- /dev/null
+++ b/BoardConfig.mk
@@ -0,0 +1,83 @@
+# Copyright (C) 2010 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.
+
+#
+# This file sets variables that control the way modules are built
+# thorughout the system. It should not be used to conditionally
+# disable makefiles (the proper mechanism to control what gets
+# included in a build is to use PRODUCT_PACKAGES in a product
+# definition file).
+#
+
+# WARNING: This line must come *before* including the proprietary
+# variant, so that it gets overwritten by the parent (which goes
+# against the traditional rules of inheritance).
+# The proprietary variant sets USE_CAMERA_STUB := false, this way
+# we use the camera stub when the vendor tree isn't present, and
+# the true camera library when the vendor tree is available.  Similarly,
+# we set USE_PROPRIETARY_AUDIO_EXTENSIONS to true in the proprietary variant as
+# well.
+USE_CAMERA_STUB := true
+USE_PROPRIETARY_AUDIO_EXTENSIONS := false
+
+# inherit from the proprietary version
+# needed for BP-flashing updater extensions
+-include vendor/moto/stingray/BoardConfigVendor.mk
+
+TARGET_BOARD_PLATFORM := tegra
+
+TARGET_CPU_ABI := armeabi-v7a
+TARGET_CPU_ABI2 := armeabi
+TARGET_CPU_SMP := true
+TARGET_ARCH_VARIANT := armv7-a
+ARCH_ARM_HAVE_TLS_REGISTER := true
+
+TARGET_USERIMAGES_USE_EXT4 := true
+
+BOARD_SYSTEMIMAGE_PARTITION_SIZE := 251658240
+BOARD_USERDATAIMAGE_PARTITION_SIZE := 31399067648
+BOARD_FLASH_BLOCK_SIZE := 4096
+
+# Wifi related defines
+BOARD_WPA_SUPPLICANT_DRIVER := WEXT
+WPA_SUPPLICANT_VERSION      := VER_0_6_X
+BOARD_WLAN_DEVICE           := bcm4329
+WIFI_DRIVER_MODULE_PATH     := "/system/lib/modules/bcm4329.ko"
+WIFI_DRIVER_FW_STA_PATH     := "/vendor/firmware/fw_bcm4329.bin"
+WIFI_DRIVER_FW_AP_PATH      := "/vendor/firmware/fw_bcm4329_apsta.bin"
+WIFI_DRIVER_MODULE_ARG      := "iface_name=wlan0 firmware_path=/vendor/firmware/fw_bcm4329.bin nvram_path=/system/etc/wifi/bcm4329.cal"
+WIFI_DRIVER_MODULE_NAME     := "bcm4329"
+WIFI_BAND                   := 802_11_ABG
+
+BOARD_USES_GENERIC_AUDIO := false
+
+BOARD_HAVE_BLUETOOTH := true
+BOARD_HAVE_BLUETOOTH_BCM := true
+
+BOARD_HAVE_GPS := true
+
+USE_OPENGL_RENDERER := true
+BOARD_EGL_CFG := device/moto/wingray/egl.cfg
+
+ifneq ($(HAVE_NVIDIA_PROP_SRC),false)
+# needed for source compilation of nvidia libraries
+-include vendor/nvidia/proprietary_src/build/definitions.mk
+-include vendor/nvidia/build/definitions.mk
+endif
+
+TARGET_RECOVERY_UI_LIB := librecovery_ui_stingray
+RECOVERY_24_BIT := true
+
+# Avoid the generation of ldrcc instructions
+NEED_WORKAROUND_CORTEX_A9_745320 := true
diff --git a/bcm4329.ko b/bcm4329.ko
new file mode 100644
index 0000000..96a5aa5
--- /dev/null
+++ b/bcm4329.ko
Binary files differ
diff --git a/board-info.txt b/board-info.txt
new file mode 100644
index 0000000..f681d73
--- /dev/null
+++ b/board-info.txt
@@ -0,0 +1,4 @@
+require mid=001
+require product=stingray
+require version-bootloader=1035
+require version-baseband=N_02.0F.00R
diff --git a/cpcap-key.kcm b/cpcap-key.kcm
new file mode 100644
index 0000000..7ee6e5a
--- /dev/null
+++ b/cpcap-key.kcm
@@ -0,0 +1,15 @@
+# Copyright (C) 2010 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.
+
+type SPECIAL_FUNCTION
diff --git a/cpcap-key.kl b/cpcap-key.kl
new file mode 100644
index 0000000..1e822d1
--- /dev/null
+++ b/cpcap-key.kl
@@ -0,0 +1,16 @@
+# Copyright (C) 2010 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.
+
+key 107   POWER             WAKE
+key 226   HEADSETHOOK       WAKE
diff --git a/device.mk b/device.mk
new file mode 100644
index 0000000..5825997
--- /dev/null
+++ b/device.mk
@@ -0,0 +1,72 @@
+#
+# Copyright (C) 2010 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.
+#
+
+DEVICE_PACKAGE_OVERLAYS := \
+    device/moto/wingray/overlay
+
+PRODUCT_PROPERTY_OVERRIDES := \
+    wifi.interface=wlan0 \
+    wifi.supplicant_scan_interval=15
+    
+include frameworks/base/build/tablet-dalvik-heap.mk
+
+PRODUCT_COPY_FILES += \
+    device/moto/wingray/bcm4329.ko:system/lib/modules/bcm4329.ko \
+    device/moto/wingray/mXT1386_08_AA.bin:system/etc/firmware/mXT1386_08_AA.bin \
+    device/moto/wingray/mXT1386_08_E1.bin:system/etc/firmware/mXT1386_08_E1.bin \
+    device/moto/wingray/mXT1386_09_AA.bin:system/etc/firmware/mXT1386_09_AA.bin \
+    device/moto/wingray/mXT1386_10_AA.bin:system/etc/firmware/mXT1386_10_AA.bin \
+    device/moto/wingray/ril/tty2ttyd:system/bin/tty2ttyd \
+    device/moto/wingray/mXT1386_10_FF.bin:system/etc/firmware/mXT1386_10_FF.bin
+
+PRODUCT_COPY_FILES += \
+    frameworks/base/data/etc/tablet_core_hardware.xml:system/etc/permissions/tablet_core_hardware.xml \
+    frameworks/base/data/etc/android.hardware.location.gps.xml:system/etc/permissions/android.hardware.location.gps.xml \
+    frameworks/base/data/etc/android.hardware.wifi.xml:system/etc/permissions/android.hardware.wifi.xml \
+    frameworks/base/data/etc/android.hardware.sensor.light.xml:system/etc/permissions/android.hardware.sensor.light.xml \
+    frameworks/base/data/etc/android.hardware.sensor.barometer.xml:system/etc/permissions/android.hardware.sensor.barometer.xml \
+    frameworks/base/data/etc/android.hardware.sensor.gyroscope.xml:system/etc/permissions/android.hardware.sensor.gyroscope.xml \
+    frameworks/base/data/etc/android.hardware.camera.flash-autofocus.xml:system/etc/permissions/android.hardware.camera.flash-autofocus.xml \
+    frameworks/base/data/etc/android.hardware.camera.front.xml:system/etc/permissions/android.hardware.camera.front.xml \
+    frameworks/base/data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml:system/etc/permissions/android.hardware.touchscreen.multitouch.jazzhand.xml \
+		frameworks/base/data/etc/android.software.sip.voip.xml:system/etc/permissions/android.software.sip.voip.xml \
+    packages/wallpapers/LivePicker/android.software.live_wallpaper.xml:system/etc/permissions/android.software.live_wallpaper.xml
+
+PRODUCT_COPY_FILES += \
+        device/moto/wingray/vold.fstab:system/etc/vold.fstab \
+        device/moto/wingray/qtouch-touchscreen.idc:system/usr/idc/qtouch-touchscreen.idc \
+        device/moto/wingray/cpcap-key.kl:system/usr/keylayout/cpcap-key.kl \
+        device/moto/wingray/cpcap-key.kcm:system/usr/keychars/cpcap-key.kcm \
+        device/moto/wingray/stingray-keypad.kl:system/usr/keylayout/stingray-keypad.kl \
+        device/moto/wingray/stingray-keypad.kcm:system/usr/keychars/stingray-keypad.kcm
+
+PRODUCT_PACKAGES := \
+    sensors.stingray \
+    lights.stingray \
+    librs_jni \
+    make_ext4fs \
+    l2ping \
+    hcitool \
+    bttest
+
+PRODUCT_CHARACTERISTICS := tablet,nosdcard
+
+# we have enough storage space to hold precise GC data
+PRODUCT_TAGS += dalvik.gc.type-precise
+
+# media config xml file
+PRODUCT_COPY_FILES += \
+    device/moto/wingray/media_profiles.xml:system/etc/media_profiles.xml
diff --git a/egl.cfg b/egl.cfg
new file mode 100644
index 0000000..a609179
--- /dev/null
+++ b/egl.cfg
@@ -0,0 +1,2 @@
+0 0 android
+0 1 tegra
diff --git a/init.stingray.rc b/init.stingray.rc
new file mode 100644
index 0000000..b5811ac
--- /dev/null
+++ b/init.stingray.rc
@@ -0,0 +1,288 @@
+on early-init
+    mount debugfs debugfs /sys/kernel/debug
+
+    export EXTERNAL_STORAGE /mnt/sdcard
+    mkdir /mnt/sdcard 0000 system system
+    # for backwards compatibility
+    symlink /mnt/sdcard /sdcard
+    mkdir /pds 0777 system system
+
+on fs
+    mount ext4 /dev/block/platform/sdhci-tegra.3/by-name/system /system wait ro
+    setprop ro.crypto.tmpfs_options size=128m,mode=0771,uid=1000,gid=1000
+    mount ext4 /dev/block/platform/sdhci-tegra.3/by-name/userdata /data wait noatime nosuid nodev
+    mount ext4 /dev/block/platform/sdhci-tegra.3/by-name/cache /cache wait noatime nosuid nodev
+    mount ext2 /dev/block/platform/sdhci-tegra.3/by-name/pdsb /pds wait ro
+
+on post-fs-data
+    mkdir /data/misc/wifi 0770 wifi wifi
+    mkdir /data/misc/wifi/sockets 0770 wifi wifi
+    mkdir /data/misc/dhcp 0770 dhcp dhcp
+    chown dhcp dhcp /data/misc/dhcp
+    mkdir /data/tpapi 0771 system system
+    mkdir /data/tpapi/etc 0771 system system
+    mkdir /data/tpapi/etc/tpa 0771 system system
+    mkdir /data/tpapi/etc/tpa/persistent 0771 system system
+
+    # cleanup obsolete symlink hack that may be lying around
+    rm /data/misc/ril
+
+    # we will remap this as /mnt/sdcard with the sdcard fuse tool
+    mkdir /data/media 0775 media_rw media_rw
+    chown media_rw media_rw /data/media
+
+    # GPS
+    #Create location directory, BRCM guci library stores LTO file and read/write
+    # config file.
+    mkdir /data/location 0770 radio radio
+
+    # Set indication (checked by vold) that we have finished this action
+    setprop vold.post_fs_data_done 1
+
+on boot
+# bluetooth
+    # power up/down interface
+    chown bluetooth bluetooth /sys/class/rfkill/rfkill0/type
+    chown bluetooth bluetooth /sys/class/rfkill/rfkill0/state
+    chmod 0660                /sys/class/rfkill/rfkill0/state
+
+    # UART device
+    chown bluetooth bluetooth /dev/ttyHS2
+    chmod 0660                /dev/ttyHS2
+
+    # bluetooth MAC address programming
+    chown bluetooth bluetooth /sys/module/board_stingray/parameters/bdaddr
+    setprop ro.bt.bdaddr_path /sys/module/board_stingray/parameters/bdaddr
+
+# Sensor
+    chown compass compass /dev/kxtf9
+    chmod 660 /dev/kxtf9
+    chown compass compass /dev/max9635
+    chmod 660 /dev/max9635
+    chown compass compass /dev/bmp085
+    chmod 660 /dev/bmp085
+    chown compass compass /dev/l3g4200d
+    chmod 660 /dev/l3g4200d
+    chown compass compass /dev/akm8975_dev
+    chmod 660 /dev/akm8975_dev
+    chown compass compass /dev/akm8975_aot
+    chmod 660 /dev/akm8975_aot
+
+# light
+    chown system system /sys/class/leds/notification-led/brightness
+    chmod 660 /sys/class/leds/notification-led/brightness
+    chown system system /sys/class/leds/notification-led/blink
+    chmod 660 /sys/class/leds/notification-led/blink
+
+    chmod 666 /dev/nvhost-ctrl
+    chmod 666 /dev/nvhost-display
+    chmod 666 /dev/nvhost-dsi
+    chmod 666 /dev/nvhost-gr2d
+    chmod 666 /dev/nvhost-gr3d
+    chmod 666 /dev/nvhost-isp
+    chmod 666 /dev/nvhost-mpe
+    chmod 666 /dev/nvhost-vi
+
+# Camera
+    chown media camera /sys/class/leds/privacy-led/brightness
+    chown media camera /sys/class/leds/flash/brightness
+    chown media camera /sys/class/leds/torch/brightness
+    chmod 660 /sys/class/leds/privacy-led/brightness
+    chmod 660 /sys/class/leds/flash/brightness
+    chmod 660 /sys/class/leds/torch/brightness
+
+# UART Device
+    chown radio radio /dev/ttyHS4
+    chmod 640 /dev/ttyHS4
+
+# broadcom 4750 device
+    chown radio radio /dev/gps_brcm4750
+    chmod 660 /dev/gps_brcm4750
+
+# Whisper UART Device
+    chown radio radio /dev/ttyHS0
+    chmod 640 /dev/ttyHS0
+
+# Whisper audio settings
+    chown media media /sys/class/switch/dock/dock_prop
+    chmod 660 /sys/class/switch/dock/dock_prop
+
+# Modem Control
+    chown radio radio /sys/class/radio/mdm6600/command
+    chmod 220 /sys/class/radio/mdm6600/command
+    chown radio radio /sys/bus/usb/devices/usb2/power/control
+    chmod 660 /sys/bus/usb/devices/usb2/power/control
+
+# Power Management Settings
+    #write /sys/devices/platform/ohci.0/usb2/2-3/power/level auto
+    write /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq 216000
+    write /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq 1000000
+    write /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor interactive
+    write /sys/devices/system/cpu/cpu1/cpufreq/scaling_min_freq 216000
+    write /sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq 1000000
+    write /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor interactive
+    write /sys/devices/system/cpu/cpufreq/interactive/go_maxspeed_load 80
+    # Wakelock debug
+    write /sys/module/wakelock/parameters/debug_mask 7
+    # No need to continuously scan w1 bus
+    write /sys/devices/w1\ bus\ master/w1_master_search 1
+    # Disable charging LED
+    write /sys/class/gpio/gpio168/value 1
+
+# Ecompass daemon
+service akmd2 /system/bin/akmd2
+    class late_start
+    user compass
+    group compass misc input
+
+service wpa_supplicant /system/bin/wpa_supplicant \
+    -Dwext -iwlan0 -c/data/misc/wifi/wpa_supplicant.conf
+#   we will start as root and wpa_supplicant will switch to user wifi
+#   after setting up the capabilities required for WEXT
+#   user wifi
+#   group wifi inet keystore
+    class main
+    socket wpa_wlan0 dgram 660 wifi wifi
+    disabled
+    oneshot
+
+service dhcpcd /system/bin/dhcpcd -ABKL
+    class main
+    disabled
+    oneshot
+
+# bugreport is triggered by the VOLUME-DOWN and VOLUME-UP keys.
+# bugtogo.sh tool will invoke bugreport and propt email composer
+# if not in user build.
+service bugreport /system/bin/bugtogo.sh
+    class main
+    disabled
+    oneshot
+    keycodes 115 114
+
+service hciattach /system/bin/brcm_patchram_plus --enable_hci --enable_lpm \
+    --baudrate 3000000 --patchram /etc/firmware/bcm4329.hcd --pcm_role slave \
+    --use_baudrate_for_download /dev/ttyHS2
+    class main
+    user bluetooth
+    group bluetooth net_bt_admin
+    disabled
+
+service location /system/bin/location
+    class late_start
+    socket gpshal_socket stream 660 radio system
+    socket location_shim stream 660 system system
+    socket gps_tcmd stream 660 radio system
+    user radio
+    group radio system mot_accy
+    oneshot
+
+service locDrv /system/bin/brcm_guci_drv -config /system/etc/gpsconfig.xml
+    class late_start
+    user radio
+    group radio inet sdcard_rw
+
+service tcmd /system/bin/tcmd
+    class main
+    oneshot
+    socket local_tcmd stream 0660 root root
+    socket batch_socket stream 0600 root root
+    disabled
+
+service ftmipcd /system/bin/ftmipcd
+    class main
+    oneshot
+    disabled
+
+# Immediately drops to user radio, after starting the real-time thread
+service whisper /system/bin/whisperd
+    class late_start
+    group radio system mot_accy
+
+# create virtual SD card at /mnt/sdcard, based on the /data/media directory
+# daemon will drop to user/group system/media_rw after initializing
+# underlying files in /data/media will be created with user and group media_rw (1023)
+service sdcard /system/bin/sdcard /data/media 1023 1023
+    class late_start
+
+service motolocation /system/bin/sh /system/bin/am startservice -n com.motorola.android.locationproxy/com.motorola.android.locationproxy.LocationProxyService
+    class late_start
+    disabled
+    oneshot
+
+on property:gsm.mot.locatonproxy=start
+    start motolocation
+
+# Get BP version and save to misc
+service savebpver /system/bin/savebpver
+   class main
+   oneshot
+   disabled
+
+on property:dev.bootcomplete=1
+   start savebpver
+
+service gadget-lte-modem /system/bin/tty2ttyd /dev/ttyACM0 /dev/ttyGS0 0 512
+    oneshot
+    disabled
+
+service gadget-qbp-modem /system/bin/tty2ttyd /dev/ttyUSB4 /dev/ttyGS1 0 1024
+    oneshot
+    disabled
+
+service gadget-qbp-diag /system/bin/tty2ttyd /dev/ttyUSB0 /dev/ttyGS2 0 1024
+    oneshot
+    disabled
+
+on property:ro.bootmode=factorycable
+    start tcmd
+    start ftmipcd
+    mount ext2 /dev/block/platform/sdhci-tegra.3/by-name/pdsb /pds wait noatime nosuid nodev remount
+    mkdir /pds/security 0771 system system
+
+on property:ro.bootmode=qbp-hw-bypass
+    setprop ril.moto-qc.usb-hw-bypass.state "on"
+
+on property:ro.bootmode=bp-tools
+    start tcmd
+    start gadget-lte-modem
+    setprop ril.moto-qc.port.diag.owner "gadget"
+
+on property:ril.moto-qc.port.diag.owner=gadget
+    start gadget-qbp-diag
+
+on property:ril.moto-qc.port.modem.owner=gadget
+    stop ril-daemon
+    start gadget-qbp-modem
+    start ril-daemon
+
+on property:ril.moto-qc.port.modem.owner=rild
+    stop gadget-qbp-modem
+    start ril-daemon
+
+service wlan_prod /system/bin/insmod /system/lib/modules/bcm4329.ko "firmware_path=/vendor/firmware/fw_bcm4329.bin nvram_path=/system/etc/wifi/bcm4329.cal"
+    group wifi mot_tcmd system
+    oneshot
+    disabled
+
+service wlan_mfg /system/bin/insmod /system/lib/modules/bcm4329.ko "firmware_path=/vendor/firmware/fw_bcm4329_mfg.bin nvram_path=/system/etc/wifi/bcm4329.cal"
+    group wifi mot_tcmd system
+    oneshot
+    disabled
+
+service wlan_unload /system/bin/rmmod bcm4329
+    group wifi mot_tcmd system
+    oneshot
+    disabled
+
+# turn on wifi for tcmd load production firmware
+on property:tcmd.load_wlan="production"
+    start wlan_prod
+
+# turn on wifi for tcmd load manufacturing firmware
+on property:tcmd.load_wlan="manufacturing"
+    start wlan_mfg
+
+# turn off wifi for tcmd
+on property:tcmd.load_wlan="unload"
+    start wlan_unload
diff --git a/kernel b/kernel
new file mode 100644
index 0000000..a4b44e9
--- /dev/null
+++ b/kernel
Binary files differ
diff --git a/libaudio/Android.mk b/libaudio/Android.mk
new file mode 100644
index 0000000..36c9696
--- /dev/null
+++ b/libaudio/Android.mk
@@ -0,0 +1,74 @@
+ifneq ($(BUILD_TINY_ANDROID),true)
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=               \
+    AudioPolicyManager.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libutils \
+    libmedia
+
+LOCAL_STATIC_LIBRARIES := libaudiopolicybase
+
+LOCAL_MODULE:= libaudiopolicy
+
+ifeq ($(BOARD_HAVE_BLUETOOTH),true)
+  LOCAL_CFLAGS += -DWITH_A2DP
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libaudio
+
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libutils \
+    libmedia \
+    libhardware_legacy
+
+ifeq ($TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
+LOCAL_LDLIBS += -ldl
+endif
+
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_SRC_FILES += AudioHardware.cpp
+
+LOCAL_CFLAGS += -fno-short-enums
+
+LOCAL_STATIC_LIBRARIES += libaudiointerface
+
+ifeq ($(USE_PROPRIETARY_AUDIO_EXTENSIONS),true)
+LOCAL_SRC_FILES += AudioPostProcessor.cpp
+LOCAL_STATIC_LIBRARIES += \
+    libEverest_motomm-r \
+    libCortexA9_aie-r \
+    libCortexA9_sas-r \
+    libCortexA9_se-r \
+    libCortexA9_motovoice-r \
+    libCortexA9_ecns-r \
+    libsamplerateconverter \
+    libCortexA9_anm-r
+
+LOCAL_CFLAGS += -DUSE_PROPRIETARY_AUDIO_EXTENSIONS
+LOCAL_C_INCLUDES += vendor/moto/stingray/motomm/ghdr
+LOCAL_C_INCLUDES += vendor/moto/stingray/motomm/rate_conv
+endif
+
+ifeq ($(BOARD_HAVE_BLUETOOTH),true)
+  LOCAL_SHARED_LIBRARIES += liba2dp
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif # not BUILD_TINY_ANDROID
+
diff --git a/libaudio/AudioHardware.cpp b/libaudio/AudioHardware.cpp
new file mode 100644
index 0000000..17969d1
--- /dev/null
+++ b/libaudio/AudioHardware.cpp
@@ -0,0 +1,1899 @@
+/*
+** Copyright 2008-2010, 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.
+*/
+
+#include <math.h>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AudioHardwareTegra"
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+
+#include "AudioHardware.h"
+#include <media/AudioRecord.h>
+
+namespace android {
+const uint32_t AudioHardware::inputSamplingRates[] = {
+    8000, 11025, 12000, 16000, 22050, 32000, 44100, 48000
+};
+
+// number of times to attempt init() before giving up
+const uint32_t MAX_INIT_TRIES = 10;
+
+// ----------------------------------------------------------------------------
+
+// always succeeds, must call init() immediately after
+AudioHardware::AudioHardware() :
+    mInit(false), mMicMute(false), mBluetoothNrec(true), mBluetoothId(0),
+    mOutput(0), /*mCurOut/InDevice*/ mCpcapCtlFd(-1), mHwOutRate(0), mHwInRate(0),
+    mMasterVol(1.0), mVoiceVol(1.0),
+    /*mCpcapGain*/
+    mSpkrVolume(-1), mMicVolume(-1)
+{
+    LOGV("AudioHardware constructor");
+}
+
+// designed to be called multiple times for retries
+status_t AudioHardware::init() {
+
+    if (mInit) {
+        return NO_ERROR;
+    }
+
+    mCpcapCtlFd = ::open("/dev/audio_ctl", O_RDWR);
+    if (mCpcapCtlFd < 0) {
+        LOGE("open /dev/audio_ctl failed: %s", strerror(errno));
+        goto error;
+    }
+
+    if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_GET_OUTPUT, &mCurOutDevice) < 0) {
+        LOGE("could not get output device: %s", strerror(errno));
+        goto error;
+    }
+    if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_GET_INPUT, &mCurInDevice) < 0) {
+        LOGE("could not get input device: %s", strerror(errno));
+        goto error;
+    }
+    // For bookkeeping only
+    if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_GET_RATE, &mHwOutRate) < 0) {
+        LOGE("could not get output rate: %s", strerror(errno));
+        goto error;
+    }
+    if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_GET_RATE, &mHwInRate) < 0) {
+        LOGE("could not get input rate: %s", strerror(errno));
+        goto error;
+    }
+
+#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
+    // Init the MM Audio Post Processing
+    mAudioPP.setAudioDev(&mCurOutDevice, &mCurInDevice, false, false, false);
+#endif
+
+    readHwGainFile();
+
+    mInit = true;
+    return NO_ERROR;
+
+error:
+    if (mCpcapCtlFd >= 0) {
+        (void) ::close(mCpcapCtlFd);
+        mCpcapCtlFd = -1;
+    }
+    return NO_INIT;
+}
+
+AudioHardware::~AudioHardware()
+{
+    LOGV("AudioHardware destructor");
+    for (size_t index = 0; index < mInputs.size(); index++) {
+        closeInputStream((AudioStreamIn*)mInputs[index]);
+    }
+    mInputs.clear();
+    closeOutputStream((AudioStreamOut*)mOutput);
+    if (mCpcapCtlFd >= 0) {
+        (void) ::close(mCpcapCtlFd);
+        mCpcapCtlFd = -1;
+    }
+}
+
+void AudioHardware::readHwGainFile()
+{
+    int fd;
+    int rc=0;
+    int i;
+    uint32_t format, version, barker;
+    fd = open("/system/etc/cpcap_gain.bin", O_RDONLY);
+    if (fd>=0) {
+        ::read(fd, &format, sizeof(uint32_t));
+        ::read(fd, &version, sizeof(uint32_t));
+        ::read(fd, &barker, sizeof(uint32_t));
+        rc = ::read(fd, mCpcapGain, sizeof(mCpcapGain));
+        LOGD("Read gain file, format %X version %X", format, version);
+        ::close(fd);
+    }
+    if (rc != sizeof(mCpcapGain) || format != 0x30303032) {
+        int gain;
+        LOGE("CPCAP gain file not valid. Using defaults.");
+        for (int i=0; i<AUDIO_HW_GAIN_NUM_DIRECTIONS; i++) {
+            if (i==AUDIO_HW_GAIN_SPKR_GAIN)
+                gain = 11;
+            else
+                gain = 31;
+            for (int j=0; j<AUDIO_HW_GAIN_NUM_USECASES; j++)
+                for (int k=0; k<AUDIO_HW_GAIN_NUM_PATHS; k++)
+                    mCpcapGain[i][j][k]=gain;
+        }
+    }
+    return;
+}
+
+status_t AudioHardware::initCheck()
+{
+    return mInit ? NO_ERROR : NO_INIT;
+}
+
+AudioStreamOut* AudioHardware::openOutputStream(
+        uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
+{
+    { // scope for the lock
+        Mutex::Autolock lock(mLock);
+
+        // only one output stream allowed
+        if (mOutput) {
+            if (status) {
+                *status = INVALID_OPERATION;
+            }
+            return 0;
+        }
+
+        // create new output stream
+        AudioStreamOutTegra* out = new AudioStreamOutTegra();
+        for (unsigned tries = 0; tries < MAX_INIT_TRIES; ++tries) {
+            if (NO_ERROR == out->init())
+                break;
+            LOGW("AudioStreamOutTegra::init failed soft, retrying");
+            sleep(1);
+        }
+        status_t lStatus;
+        lStatus = out->initCheck();
+        if (NO_ERROR != lStatus) {
+            LOGE("AudioStreamOutTegra::init failed hard");
+        } else {
+            lStatus = out->set(this, devices, format, channels, sampleRate);
+        }
+        if (status) {
+            *status = lStatus;
+        }
+        if (lStatus == NO_ERROR) {
+            mOutput = out;
+        } else {
+            mLock.unlock();
+            delete out;
+            out = NULL;
+            mLock.lock();
+        }
+    }
+    return mOutput;
+}
+
+void AudioHardware::closeOutputStream(AudioStreamOut* out) {
+    Mutex::Autolock lock(mLock);
+    if (mOutput == 0 || mOutput != out) {
+        LOGW("Attempt to close invalid output stream");
+    }
+    else {
+        // AudioStreamOutTegra destructor calls standby which locks
+        mOutput = 0;
+        mLock.unlock();
+        delete out;
+        mLock.lock();
+    }
+}
+
+AudioStreamIn* AudioHardware::openInputStream(
+        uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status,
+        AudioSystem::audio_in_acoustics acoustic_flags)
+{
+    // check for valid input source
+    if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) {
+        return 0;
+    }
+
+    Mutex::Autolock lock(mLock);
+
+    AudioStreamInTegra* in = new AudioStreamInTegra();
+    // this serves a similar purpose as init()
+    status_t lStatus = in->set(this, devices, format, channels, sampleRate, acoustic_flags);
+    if (status) {
+        *status = lStatus;
+    }
+    if (lStatus != NO_ERROR) {
+        mLock.unlock();
+        delete in;
+        mLock.lock();
+        return 0;
+    }
+
+    mInputs.add(in);
+
+    return in;
+}
+
+void AudioHardware::closeInputStream(AudioStreamIn* in)
+{
+    Mutex::Autolock lock(mLock);
+
+    ssize_t index = mInputs.indexOf((AudioStreamInTegra *)in);
+    if (index < 0) {
+        LOGW("Attempt to close invalid input stream");
+    } else {
+        mInputs.removeAt(index);
+        mLock.unlock();
+        delete in;
+        mLock.lock();
+    }
+}
+
+status_t AudioHardware::setMode(int mode)
+{
+    AutoMutex lock(mLock);
+    bool wasInCall = isInCall();
+    LOGV("setMode() : new %d, old %d", mode, mMode);
+    status_t status = AudioHardwareBase::setMode(mode);
+    if (status == NO_ERROR) {
+        if (wasInCall ^ isInCall()) {
+            doRouting_l();
+            if (wasInCall) {
+                setMicMute_l(false);
+            }
+        }
+    }
+
+    return status;
+}
+
+// Must be called with mLock held
+status_t AudioHardware::doStandby(int stop_fd, bool output, bool enable)
+{
+    status_t status = NO_ERROR;
+    struct cpcap_audio_stream standby;
+
+    LOGV("AudioHardware::doStandby() putting %s in %s mode",
+            output ? "output" : "input",
+            enable ? "standby" : "online" );
+
+// Debug code
+    if (!mLock.tryLock()) {
+        LOGE("doStandby called without mLock held.");
+        mLock.unlock();
+    }
+// end Debug code
+
+    if (output) {
+        standby.id = CPCAP_AUDIO_OUT_STANDBY;
+        standby.on = enable;
+
+        if (enable) {
+            /* Flush the queued playback data.  Putting the output in standby
+             * will cause CPCAP to not drive the i2s interface, and write()
+             * will block until playback is resumed.
+             */
+            if (mOutput)
+                mOutput->flush();
+        }
+
+        if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_SET_OUTPUT, &standby) < 0) {
+            LOGE("could not turn off current output device: %s",
+                 strerror(errno));
+            status = errno;
+        }
+
+        if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_GET_OUTPUT, &mCurOutDevice) < 0) {
+            LOGE("could not get current output device after standby: %s",
+                 strerror(errno));
+        }
+        LOGV("%s: after standby %s, output device %d is %s", __FUNCTION__,
+             enable ? "enable" : "disable", mCurOutDevice.id,
+             mCurOutDevice.on ? "on" : "off");
+    } else {
+        standby.id = CPCAP_AUDIO_IN_STANDBY;
+        standby.on = enable;
+
+        if (enable && stop_fd >= 0) {
+            /* Stop recording, if ongoing.  Muting the microphone will cause
+             * CPCAP to not send data through the i2s interface, and read()
+             * will block until recording is resumed.
+             */
+            LOGV("%s: stop recording", __FUNCTION__);
+            if (::ioctl(stop_fd, TEGRA_AUDIO_IN_STOP) < 0) {
+                LOGE("could not stop recording: %s",
+                     strerror(errno));
+            }
+        }
+
+        if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_SET_INPUT, &standby) < 0) {
+            LOGE("could not turn off current input device: %s",
+                 strerror(errno));
+            status = errno;
+        }
+        ::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_GET_INPUT, &mCurInDevice);
+        LOGV("%s: after standby %s, input device %d is %s", __FUNCTION__,
+             enable ? "enable" : "disable", mCurInDevice.id,
+             mCurInDevice.on ? "on" : "off");
+    }
+
+    return status;
+}
+
+status_t AudioHardware::setMicMute(bool state)
+{
+    Mutex::Autolock lock(mLock);
+    return setMicMute_l(state);
+}
+
+status_t AudioHardware::setMicMute_l(bool state)
+{
+    if (mMicMute != state) {
+        mMicMute = state;
+        LOGV("setMicMute() %s", (state)?"ON":"OFF");
+    }
+    return NO_ERROR;
+}
+
+status_t AudioHardware::getMicMute(bool* state)
+{
+    *state = mMicMute;
+    return NO_ERROR;
+}
+
+status_t AudioHardware::setParameters(const String8& keyValuePairs)
+{
+    AudioParameter param = AudioParameter(keyValuePairs);
+    String8 value;
+    String8 key;
+    const char BT_NREC_KEY[] = "bt_headset_nrec";
+    const char BT_NAME_KEY[] = "bt_headset_name";
+    const char BT_NREC_VALUE_ON[] = "on";
+
+
+    LOGV("setParameters() %s", keyValuePairs.string());
+
+    if (keyValuePairs.length() == 0) return BAD_VALUE;
+
+    key = String8(BT_NREC_KEY);
+    if (param.get(key, value) == NO_ERROR) {
+        if (value == BT_NREC_VALUE_ON) {
+            mBluetoothNrec = true;
+            LOGI("Turn on bluetooth NREC");
+        } else {
+            mBluetoothNrec = false;
+            LOGI("Turning noise reduction and echo cancellation off for BT "
+                 "headset");
+        }
+        doRouting();
+    }
+    key = String8(BT_NAME_KEY);
+    if (param.get(key, value) == NO_ERROR) {
+        mBluetoothId = 0;
+#if 0
+        for (int i = 0; i < mNumSndEndpoints; i++) {
+            if (!strcasecmp(value.string(), mSndEndpoints[i].name)) {
+                mBluetoothId = mSndEndpoints[i].id;
+                LOGI("Using custom acoustic parameters for %s", value.string());
+                break;
+            }
+        }
+#endif
+        if (mBluetoothId == 0) {
+            LOGI("Using default acoustic parameters "
+                 "(%s not in acoustic database)", value.string());
+            doRouting();
+        }
+    }
+    return NO_ERROR;
+}
+
+String8 AudioHardware::getParameters(const String8& keys)
+{
+    AudioParameter request = AudioParameter(keys);
+    AudioParameter reply = AudioParameter();
+    String8 value;
+    String8 key;
+
+    LOGV("getParameters() %s", keys.string());
+
+#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
+    key = "ec_supported";
+    if (request.get(key, value) == NO_ERROR) {
+        value = "yes";
+        reply.add(key, value);
+    }
+#endif
+
+    return reply.toString();
+}
+
+size_t AudioHardware::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
+{
+    size_t bufsize;
+
+    if (format != AudioSystem::PCM_16_BIT) {
+        LOGW("getInputBufferSize bad format: %d", format);
+        return 0;
+    }
+    if (channelCount < 1 || channelCount > 2) {
+        LOGW("getInputBufferSize bad channel count: %d", channelCount);
+        return 0;
+    }
+
+    // Return 20 msec input buffer size.
+    bufsize = sampleRate * sizeof(int16_t) * channelCount / 50;
+    if (bufsize & 0x7) {
+       // Not divisible by 8.
+       bufsize +=8;
+       bufsize &= ~0x7;
+    }
+    LOGD("%s: returns %d for rate %d", __FUNCTION__, bufsize, sampleRate);
+    return bufsize;
+}
+
+//setVoiceVolume is only useful for setting sidetone gains with a baseband
+//controlling volume.  Don't adjust hardware volume with this API.
+//
+//(On Stingray, don't use mVoiceVol for anything.)
+status_t AudioHardware::setVoiceVolume(float v)
+{
+    if (v < 0.0)
+        v = 0.0;
+    else if (v > 1.0)
+        v = 1.0;
+
+    LOGI("Setting unused in-call vol to %f",v);
+    mVoiceVol = v;
+
+    return NO_ERROR;
+}
+
+status_t AudioHardware::setMasterVolume(float v)
+{
+    if (v < 0.0)
+        v = 0.0;
+    else if (v > 1.0)
+        v = 1.0;
+
+    LOGV("Set master vol to %f.", v);
+    mMasterVol = v;
+    Mutex::Autolock lock(mLock);
+    int useCase = AUDIO_HW_GAIN_USECASE_MM;
+    AudioStreamInTegra *input = getActiveInput_l();
+    if (input) {
+        if (isInCall() && mOutput && !mOutput->getStandby() &&
+                input->source() == AUDIO_SOURCE_VOICE_COMMUNICATION) {
+            useCase = AUDIO_HW_GAIN_USECASE_VOICE;
+        } else if (input->source() == AUDIO_SOURCE_VOICE_RECOGNITION) {
+            useCase = AUDIO_HW_GAIN_USECASE_VOICE_REC;
+        }
+    }
+    setVolume_l(v, useCase);
+    return NO_ERROR;
+}
+
+// Call with mLock held.
+status_t AudioHardware::setVolume_l(float v, int usecase)
+{
+    int spkr = getGain(AUDIO_HW_GAIN_SPKR_GAIN, usecase);
+    int mic = getGain(AUDIO_HW_GAIN_MIC_GAIN, usecase);
+
+    if (spkr==0) {
+       // no device to set volume on.  Ignore request.
+       return -1;
+    }
+
+    spkr = ceil(v * spkr);
+    if (mSpkrVolume != spkr) {
+        LOGD("Set tx volume to %d", spkr);
+        int ret = ::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_SET_VOLUME, spkr);
+        if (ret < 0) {
+            LOGE("could not set spkr volume: %s", strerror(errno));
+            return ret;
+        }
+        mSpkrVolume = spkr;
+    }
+    if (mMicVolume != mic) {
+        LOGD("Set rx volume to %d", mic);
+        int ret = ::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_SET_VOLUME, mic);
+        if (ret < 0) {
+            LOGE("could not set mic volume: %s", strerror(errno));
+            return ret;
+        }
+        mMicVolume = mic;
+    }
+
+    return NO_ERROR;
+}
+
+uint8_t AudioHardware::getGain(int direction, int usecase)
+{
+    int path;
+    AudioStreamInTegra *input = getActiveInput_l();
+    uint32_t inDev = (input == NULL) ? 0 : input->devices();
+    if (!mOutput) {
+       LOGE("No output device.");
+       return 0;
+    }
+    uint32_t outDev = mOutput->devices();
+
+// In case of an actual phone, with an actual earpiece, uncomment.
+//    if (outDev & AudioSystem::DEVICE_OUT_EARPIECE)
+//        path = AUDIO_HW_GAIN_EARPIECE;
+//    else
+    if (outDev & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)
+        path = AUDIO_HW_GAIN_HEADSET_NO_MIC;
+    else if (outDev & AudioSystem::DEVICE_OUT_WIRED_HEADSET)
+        path = AUDIO_HW_GAIN_HEADSET_W_MIC;
+    else if (outDev & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET)
+        path = AUDIO_HW_GAIN_EMU_DEVICE;
+    else
+       path = AUDIO_HW_GAIN_SPEAKERPHONE;
+
+    LOGV("Picked gain[%d][%d][%d] which is %d.",direction, usecase, path,
+          mCpcapGain[direction][usecase][path]);
+
+    return mCpcapGain[direction][usecase][path];
+}
+
+int AudioHardware::getActiveInputRate()
+{
+    AudioStreamInTegra *input = getActiveInput_l();
+    return (input != NULL) ? input->sampleRate() : 0;
+}
+
+status_t AudioHardware::doRouting()
+{
+    Mutex::Autolock lock(mLock);
+    return doRouting_l();
+}
+
+// Call this with mLock held.
+status_t AudioHardware::doRouting_l()
+{
+    if (!mOutput) {
+        return NO_ERROR;
+    }
+    uint32_t outputDevices = mOutput->devices();
+    AudioStreamInTegra *input = getActiveInput_l();
+    uint32_t inputDevice = (input == NULL) ? 0 : input->devices();
+    uint32_t btScoOutDevices = outputDevices & (
+                           AudioSystem::DEVICE_OUT_BLUETOOTH_SCO |
+                           AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
+                           AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT );
+    uint32_t spdifOutDevices = outputDevices & (
+                           AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET |
+                           AudioSystem::DEVICE_OUT_AUX_DIGITAL );
+    uint32_t speakerOutDevices = outputDevices ^ btScoOutDevices ^ spdifOutDevices;
+    uint32_t btScoInDevice = inputDevice & AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET;
+    uint32_t micInDevice   = inputDevice ^ btScoInDevice;
+    int sndOutDevice = -1;
+    int sndInDevice = -1;
+    bool btScoOn = btScoOutDevices||btScoInDevice;
+
+    LOGV("%s: inputDevice %x, outputDevices %x", __FUNCTION__,
+         inputDevice, outputDevices);
+
+    switch (inputDevice) {
+    case AudioSystem::DEVICE_IN_DEFAULT:
+    case AudioSystem::DEVICE_IN_BUILTIN_MIC:
+        sndInDevice = CPCAP_AUDIO_IN_MIC1;
+        break;
+    case AudioSystem::DEVICE_IN_WIRED_HEADSET:
+        sndInDevice = CPCAP_AUDIO_IN_MIC2;
+        break;
+    default:
+        break;
+    }
+
+    switch (speakerOutDevices) {
+    case AudioSystem::DEVICE_OUT_EARPIECE:
+    case AudioSystem::DEVICE_OUT_DEFAULT:
+    case AudioSystem::DEVICE_OUT_SPEAKER:
+        sndOutDevice = CPCAP_AUDIO_OUT_SPEAKER;
+        break;
+    case AudioSystem::DEVICE_OUT_WIRED_HEADSET:
+    case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE:
+        sndOutDevice = CPCAP_AUDIO_OUT_HEADSET;
+        break;
+    case AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADSET:
+    case AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADPHONE:
+        sndOutDevice = CPCAP_AUDIO_OUT_HEADSET_AND_SPEAKER;
+        break;
+    case AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET:
+        sndOutDevice = CPCAP_AUDIO_OUT_ANLG_DOCK_HEADSET;
+        break;
+    case AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET:
+      // To be implemented
+        break;
+    default:
+        break;
+    }
+
+    if (sndInDevice != (int)mCurInDevice.id) {
+        if (sndInDevice == -1) {
+            LOGV("input device set %x not supported, defaulting to on-board mic",
+                 inputDevice);
+            mCurInDevice.id = CPCAP_AUDIO_IN_MIC1;
+        }
+        else
+            mCurInDevice.id = sndInDevice;
+
+        if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_SET_INPUT,
+                  &mCurInDevice) < 0)
+            LOGE("could not set input (%d, on %d): %s",
+                 mCurInDevice.id, mCurInDevice.on, strerror(errno));
+
+        LOGV("current input %d, %s",
+             mCurInDevice.id,
+             mCurInDevice.on ? "on" : "off");
+    }
+
+    if (sndOutDevice != (int)mCurOutDevice.id) {
+        if (sndOutDevice == -1) {
+            LOGW("output device set %x not supported, defaulting to speaker",
+                 outputDevices);
+            mCurOutDevice.id = CPCAP_AUDIO_OUT_SPEAKER;
+        }
+        else
+            mCurOutDevice.id = sndOutDevice;
+
+        if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_SET_OUTPUT,
+                  &mCurOutDevice) < 0)
+            LOGE("could not set output (%d, on %d): %s",
+                 mCurOutDevice.id, mCurOutDevice.on,
+                 strerror(errno));
+
+        LOGV("current output %d, %s",
+             mCurOutDevice.id,
+             mCurOutDevice.on ? "on" : "off");
+    }
+
+    bool ecnsEnabled = false;
+    // enable EC if:
+    // - the audio mode is IN_CALL or IN_COMMUNICATION  AND
+    // - the output stream is active AND
+    // - an input stream with VOICE_COMMUNICATION source is active
+    if (isInCall() && !mOutput->getStandby() &&
+        input && input->source() == AUDIO_SOURCE_VOICE_COMMUNICATION) {
+        ecnsEnabled = true;
+    }
+
+    int oldInRate=mHwInRate, oldOutRate=mHwOutRate;
+#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
+    int ecnsRate = getActiveInputRate() < 16000? 8000 : 16000;
+    // Check input/output rates for HW.
+    if (ecnsEnabled) {
+        mHwInRate = ecnsRate;
+        mHwOutRate = mHwInRate;
+        LOGD("EC/NS active, requests rate as %d for in/out", mHwInRate);
+    }
+    else
+#endif
+    {
+        mHwInRate = getActiveInputRate();
+        mHwOutRate = AUDIO_HW_OUT_SAMPLERATE;
+        LOGV("No EC/NS, set input rate %d, output %d.", mHwInRate, mHwOutRate);
+    }
+    if (btScoOn) {
+        mHwOutRate = 8000;
+        mHwInRate = 8000;
+        LOGD("Bluetooth SCO active, rate forced to 8K");
+    }
+
+    if (input) {
+        // acquire mutex if not already locked by read()
+        if (!input->isLocked()) {
+            input->lock();
+        }
+        if (mHwInRate != oldInRate) {
+            LOGV("Minor TODO: Flush input if active.");
+            if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_SET_RATE,
+                      mHwInRate) < 0)
+                LOGE("could not set input rate(%d): %s",
+                      mHwInRate, strerror(errno));
+            if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_GET_RATE, &mHwInRate))
+                LOGE("CPCAP driver error reading rates: %s", strerror(errno));
+        }
+    }
+
+    // acquire mutex if not already locked by write()
+    if (!mOutput->isLocked()) {
+        mOutput->lock();
+    }
+    int speakerOutRate = 0;
+    if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_GET_RATE, &speakerOutRate))
+        LOGE("could not read output rate: %s",
+                   strerror(errno));
+    if (mHwOutRate != oldOutRate ||
+        (speakerOutRate!=AUDIO_HW_OUT_SAMPLERATE && btScoOn)) {
+        int speaker_rate = mHwOutRate;
+        if (btScoOn) {
+            speaker_rate = AUDIO_HW_OUT_SAMPLERATE;
+        }
+        // Flush old data (wrong rate) from I2S driver before changing rate.
+        if (mOutput) {
+            mOutput->flush();
+            if (ecnsEnabled) {
+                mOutput->setNumBufs(AUDIO_HW_NUM_OUT_BUF);
+            } else {
+                mOutput->setNumBufs(AUDIO_HW_NUM_OUT_BUF_LONG);
+            }
+        }
+        // Now the DMA is empty, change the rate.
+        if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_SET_RATE,
+                  speaker_rate) < 0)
+            LOGE("could not set output rate(%d): %s",
+                  speaker_rate, strerror(errno));
+    }
+#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
+    mAudioPP.setAudioDev(&mCurOutDevice, &mCurInDevice,
+                         btScoOn, mBluetoothNrec,
+                         spdifOutDevices?true:false);
+    mAudioPP.enableEcns(ecnsEnabled);
+#endif
+
+    mOutput->setDriver_l(speakerOutDevices?true:false,
+                       btScoOn,
+                       spdifOutDevices?true:false, mHwOutRate);
+    if (input) {
+        input->setDriver_l(micInDevice?true:false,
+                         btScoInDevice?true:false, mHwInRate);
+    }
+    if (!mOutput->isLocked()) {
+        mOutput->unlock();
+    }
+    if (input && !input->isLocked()) {
+        input->unlock();
+    }
+
+    // Since HW path may have changed, set the hardware gains.
+    int useCase = AUDIO_HW_GAIN_USECASE_MM;
+    if (ecnsEnabled) {
+        useCase = AUDIO_HW_GAIN_USECASE_VOICE;
+    } else if (input && input->source() == AUDIO_SOURCE_VOICE_RECOGNITION) {
+        useCase = AUDIO_HW_GAIN_USECASE_VOICE_REC;
+    }
+    setVolume_l(mMasterVol, useCase);
+
+    return NO_ERROR;
+}
+
+status_t AudioHardware::dumpInternals(int fd, const Vector<String16>& args)
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+    result.append("AudioHardware::dumpInternals\n");
+    snprintf(buffer, SIZE, "\tmInit: %s\n", mInit? "true": "false");
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\tmMicMute: %s\n", mMicMute? "true": "false");
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\tmBluetoothNrec: %s\n", mBluetoothNrec? "true": "false");
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\tmBluetoothId: %d\n", mBluetoothId);
+    result.append(buffer);
+    ::write(fd, result.string(), result.size());
+    return NO_ERROR;
+}
+
+status_t AudioHardware::dump(int fd, const Vector<String16>& args)
+{
+    dumpInternals(fd, args);
+    for (size_t index = 0; index < mInputs.size(); index++) {
+        mInputs[index]->dump(fd, args);
+    }
+
+    if (mOutput) {
+        mOutput->dump(fd, args);
+    }
+    return NO_ERROR;
+}
+
+uint32_t AudioHardware::getInputSampleRate(uint32_t sampleRate)
+{
+    uint32_t i;
+    uint32_t prevDelta;
+    uint32_t delta;
+
+    for (i = 0, prevDelta = 0xFFFFFFFF; i < sizeof(inputSamplingRates)/sizeof(uint32_t); i++, prevDelta = delta) {
+        delta = abs(sampleRate - inputSamplingRates[i]);
+        if (delta > prevDelta) break;
+    }
+    // i is always > 0 here
+    return inputSamplingRates[i-1];
+}
+
+// getActiveInput_l() must be called with mLock held
+AudioHardware::AudioStreamInTegra *AudioHardware::getActiveInput_l()
+{
+    for (size_t i = 0; i < mInputs.size(); i++) {
+        // return first input found not being in standby mode
+        // as only one input can be in this state
+        if (!mInputs[i]->getStandby()) {
+            return mInputs[i];
+        }
+    }
+
+    return NULL;
+}
+
+// ----------------------------------------------------------------------------
+// Sample Rate Converter wrapper
+//
+#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
+AudioHardware::AudioStreamSrc::AudioStreamSrc() :
+  mSrcInitted(false)
+{
+}
+AudioHardware::AudioStreamSrc::~AudioStreamSrc()
+{
+}
+
+void AudioHardware::AudioStreamSrc::init(int inRate, int outRate)
+{
+    if (mSrcBuffer == NULL)
+        mSrcBuffer = new char[src_memory_required_stereo(MAX_FRAME_LEN, MAX_CONVERT_RATIO)];
+    if (mSrcBuffer == NULL) {
+        LOGE("Failed to allocate memory for sample rate converter.");
+        return;
+    }
+    mSrcInit.memory = (SRC16*)(mSrcBuffer);
+    mSrcInit.input_rate = inRate;
+    mSrcInit.output_rate = outRate;
+    mSrcInit.frame_length = MAX_FRAME_LEN;
+    mSrcInit.stereo_flag = SRC_OFF;
+    mSrcInit.input_interleaved = SRC_OFF;
+    mSrcInit.output_interleaved = SRC_OFF;
+    rate_convert_init(&mSrcInit, &mSrcObj);
+
+    mSrcInitted = true;
+    mSrcInRate = inRate;
+    mSrcOutRate = outRate;
+}
+#endif
+
+// ----------------------------------------------------------------------------
+
+// always succeeds, must call init() immediately after
+AudioHardware::AudioStreamOutTegra::AudioStreamOutTegra() :
+    mHardware(0), mFd(-1), mFdCtl(-1),
+    mBtFd(-1), mBtFdCtl(-1), mBtFdIoCtl(-1),
+    mSpdifFd(-1), mSpdifFdCtl(-1),
+    mStartCount(0), mRetryCount(0), mDevices(0),
+    mIsSpkrEnabled(false), mIsBtEnabled(false), mIsSpdifEnabled(false),
+    mIsSpkrEnabledReq(false), mIsBtEnabledReq(false), mIsSpdifEnabledReq(false),
+    mSpareSample(0), mHaveSpareSample(false),
+    mState(AUDIO_STREAM_IDLE), /*mSrc*/ mLocked(false), mDriverRate(AUDIO_HW_OUT_SAMPLERATE),
+    mInit(false)
+{
+    LOGV("AudioStreamOutTegra constructor");
+}
+
+// designed to be called multiple times for retries
+status_t AudioHardware::AudioStreamOutTegra::init()
+{
+    if (mInit) {
+        return NO_ERROR;
+    }
+
+#define OPEN_FD(fd, dev)    fd = ::open(dev, O_RDWR);                              \
+                            if (fd < 0) {                                          \
+                                LOGE("open " dev " failed: %s", strerror(errno));   \
+                                goto error;                                         \
+                            }
+    OPEN_FD(mFd, "/dev/audio0_out")
+    OPEN_FD(mFdCtl, "/dev/audio0_out_ctl")
+    OPEN_FD(mBtFd, "/dev/audio1_out")
+    OPEN_FD(mBtFdCtl, "/dev/audio1_out_ctl")
+    OPEN_FD(mBtFdIoCtl, "/dev/audio1_ctl")
+    // may need to be changed to warnings
+    OPEN_FD(mSpdifFd, "/dev/spdif_out")
+    OPEN_FD(mSpdifFdCtl, "/dev/spdif_out_ctl")
+#undef OPEN_FD
+
+    setNumBufs(AUDIO_HW_NUM_OUT_BUF_LONG);
+
+    mInit = true;
+    return NO_ERROR;
+
+error:
+#define CLOSE_FD(fd)    if (fd >= 0) {          \
+                            (void) ::close(fd); \
+                            fd = -1;            \
+                        }
+    CLOSE_FD(mFd)
+    CLOSE_FD(mFdCtl)
+    CLOSE_FD(mBtFd)
+    CLOSE_FD(mBtFdCtl)
+    CLOSE_FD(mBtFdIoCtl)
+    CLOSE_FD(mSpdifFd)
+    CLOSE_FD(mSpdifFdCtl)
+#undef CLOSE_FD
+    return NO_INIT;
+}
+
+status_t AudioHardware::AudioStreamOutTegra::initCheck()
+{
+    return mInit ? NO_ERROR : NO_INIT;
+}
+
+// Called with mHardware->mLock and mLock held.
+void AudioHardware::AudioStreamOutTegra::setDriver_l(
+        bool speaker, bool bluetooth, bool spdif, int sampleRate)
+{
+    int bit_format = TEGRA_AUDIO_BIT_FORMAT_DEFAULT;
+    bool is_bt_bypass = false;
+
+    LOGV("%s: Analog speaker? %s. Bluetooth? %s. S/PDIF? %s. sampleRate %d", __FUNCTION__,
+        speaker?"yes":"no", bluetooth?"yes":"no", spdif?"yes":"no", sampleRate);
+
+    // force some reconfiguration at next write()
+    if (mState == AUDIO_STREAM_CONFIGURED) {
+        if (mIsSpkrEnabled != speaker || mIsBtEnabled != bluetooth || mIsSpdifEnabled != spdif) {
+            mState = AUDIO_STREAM_CONFIG_REQ;
+        } else if (sampleRate != mDriverRate) {
+            mState = AUDIO_STREAM_NEW_RATE_REQ;
+        }
+    }
+
+    mIsSpkrEnabledReq = speaker;
+    mIsBtEnabledReq = bluetooth;
+    mIsSpdifEnabledReq = spdif;
+
+}
+
+status_t AudioHardware::AudioStreamOutTegra::set(
+        AudioHardware* hw, uint32_t devices, int *pFormat, uint32_t *pChannels, uint32_t *pRate)
+{
+    int lFormat = pFormat ? *pFormat : 0;
+    uint32_t lChannels = pChannels ? *pChannels : 0;
+    uint32_t lRate = pRate ? *pRate : 0;
+
+    mHardware = hw;
+
+    // fix up defaults
+    if (lFormat == 0) lFormat = format();
+    if (lChannels == 0) lChannels = channels();
+    if (lRate == 0) lRate = sampleRate();
+
+    // check values
+    if ((lFormat != format()) ||
+        (lChannels != channels()) ||
+        (lRate != sampleRate())) {
+        if (pFormat) *pFormat = format();
+        if (pChannels) *pChannels = channels();
+        if (pRate) *pRate = sampleRate();
+        return BAD_VALUE;
+    }
+
+#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
+    mHardware->mAudioPP.setPlayAudioRate(lRate);
+#endif
+
+    if (pFormat) *pFormat = lFormat;
+    if (pChannels) *pChannels = lChannels;
+    if (pRate) *pRate = lRate;
+
+    mDevices = devices;
+    if (mFd >= 0 && mFdCtl >= 0 &&
+                mBtFd >= 0 &&
+                mBtFdCtl >= 0 &&
+                mBtFdIoCtl >= 0) {
+        if (mSpdifFd < 0 || mSpdifFdCtl < 0)
+            LOGW("s/pdif driver not present");
+        return NO_ERROR;
+    } else {
+        LOGE("Problem opening device files - Is your kernel compatible?");
+        return NO_INIT;
+    }
+}
+
+AudioHardware::AudioStreamOutTegra::~AudioStreamOutTegra()
+{
+    standby();
+    // Prevent someone from flushing the fd during a close.
+    Mutex::Autolock lock(mFdLock);
+    if (mFd >= 0)         { ::close(mFd);         mFd = -1;         }
+    if (mFdCtl >= 0)      { ::close(mFdCtl);      mFdCtl = -1;      }
+    if (mBtFd >= 0)       { ::close(mBtFd);       mBtFd = -1;       }
+    if (mBtFdCtl >= 0)    { ::close(mBtFdCtl);    mBtFdCtl = -1;    }
+    if (mBtFdIoCtl >= 0)  { ::close(mBtFdIoCtl);  mBtFdIoCtl = -1;  }
+    if (mSpdifFd >= 0)    { ::close(mSpdifFd);    mSpdifFd = -1;    }
+    if (mSpdifFdCtl >= 0) { ::close(mSpdifFdCtl); mSpdifFdCtl = -1; }
+}
+
+ssize_t AudioHardware::AudioStreamOutTegra::write(const void* buffer, size_t bytes)
+{
+    status_t status;
+    if (!mHardware) {
+        LOGE("%s: mHardware is null", __FUNCTION__);
+        return NO_INIT;
+    }
+    // LOGD("AudioStreamOutTegra::write(%p, %u) TID %d", buffer, bytes, gettid());
+    // Protect output state during the write process.
+
+    bool needsOnline = false;
+    if (mState < AUDIO_STREAM_CONFIGURED) {
+        mHardware->mLock.lock();
+        if (mState < AUDIO_STREAM_CONFIGURED) {
+            needsOnline = true;
+        } else {
+            mHardware->mLock.unlock();
+        }
+    }
+
+    { // scope for the lock
+        Mutex::Autolock lock(mLock);
+
+        ssize_t written = 0;
+        const uint8_t* p = static_cast<const uint8_t*>(buffer);
+        size_t outsize = bytes;
+        int outFd = mFd;
+        bool stereo;
+        ssize_t writtenToSpdif = 0;
+
+        if (needsOnline) {
+            status = online_l();
+            mHardware->mLock.unlock();
+            if (status < 0) {
+                goto error;
+            }
+        }
+        stereo = mIsBtEnabled ? false : (channels() == AudioSystem::CHANNEL_OUT_STEREO);
+
+#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
+        // Do Multimedia processing if appropriate for device and usecase.
+        mHardware->mAudioPP.doMmProcessing((void *)buffer, bytes / frameSize());
+#endif
+
+        if (mIsSpkrEnabled && mIsBtEnabled) {
+            // When dual routing to CPCAP and Bluetooth, piggyback CPCAP audio now,
+            // and then down convert for the BT.
+            // CPCAP is always 44.1 in this case.
+            // This also works in the three-way routing case.
+            Mutex::Autolock lock2(mFdLock);
+            ::write(outFd, buffer, outsize);
+        }
+        if (mIsSpdifEnabled) {
+            // When dual routing to Speaker and HDMI, piggyback HDMI now, since it
+            // has no mic we'll leave the rest of the acoustic processing for the
+            // CPCAP hardware path.
+            // This also works in the three-way routing case, except the acoustic
+            // tuning will be done on Bluetooth, since it has the exclusive mic amd
+            // it also needs the sample rate conversion
+            Mutex::Autolock lock2(mFdLock);
+            if (mSpdifFd >= 0) {
+                writtenToSpdif = ::write(mSpdifFd, buffer, outsize);
+                LOGV("%s: written %d bytes to SPDIF", __FUNCTION__, (int)writtenToSpdif);
+            } else {
+                LOGW("s/pdif enabled but unavailable");
+            }
+        }
+        if (mIsBtEnabled) {
+            outFd = mBtFd;
+        } else if (mIsSpdifEnabled && !mIsSpkrEnabled) {
+            outFd = -1;
+        }
+
+#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
+        // Check if sample rate conversion or ECNS are required.
+        // Caution: Upconversion (from 44.1 to 48) would require a new output buffer larger than the
+        // original one.
+        if (mDriverRate != (int)sampleRate()) {
+            if (!mSrc.initted() ||
+                 mSrc.inRate() != (int)sampleRate() ||
+                 mSrc.outRate() != mDriverRate) {
+                LOGI("%s: downconvert started from %d to %d",__FUNCTION__,
+                     sampleRate(), mDriverRate);
+                mSrc.init(sampleRate(), mDriverRate);
+                if (!mSrc.initted()) {
+                    status = -1;
+                    goto error;
+                }
+                // Workaround to give multiple of 4 bytes to driver: Keep one sample
+                // buffered in case SRC returns an odd number of samples.
+                mHaveSpareSample = false;
+            }
+        } else {
+            mSrc.deinit();
+        }
+
+        if (mHardware->mAudioPP.isEcnsEnabled() || mSrc.initted())
+        {
+            // cut audio down to Mono for SRC or ECNS
+            if (channels() == AudioSystem::CHANNEL_OUT_STEREO)
+            {
+                // Do stereo-to-mono downmix before SRC, in-place
+                int16_t *destBuf = (int16_t *) buffer;
+                for (int i = 0; i < (int)bytes/2; i++) {
+                     destBuf[i] = (destBuf[i*2]>>1) + (destBuf[i*2+1]>>1);
+                }
+                outsize >>= 1;
+            }
+        }
+
+        if (mSrc.initted()) {
+            // Apply the sample rate conversion.
+            mSrc.mIoData.in_buf_ch1 = (SRC16 *) (buffer);
+            mSrc.mIoData.in_buf_ch2 = 0;
+            mSrc.mIoData.input_count = outsize / sizeof(SRC16);
+            mSrc.mIoData.out_buf_ch1 = (SRC16 *) (buffer);
+            mSrc.mIoData.out_buf_ch2 = 0;
+            mSrc.mIoData.output_count = outsize / sizeof(SRC16);
+            if (mHaveSpareSample) {
+                // Leave room for placing the spare.
+                mSrc.mIoData.out_buf_ch1++;
+                mSrc.mIoData.output_count--;
+            }
+            mSrc.srcConvert();
+            LOGV("Converted %d bytes at %d to %d bytes at %d",
+                 outsize, sampleRate(), mSrc.mIoData.output_count*2, mDriverRate);
+            if (mHaveSpareSample) {
+                int16_t *bufp = (int16_t*)buffer;
+                bufp[0]=mSpareSample;
+                mSrc.mIoData.output_count++;
+                mHaveSpareSample = false;
+            }
+            outsize = mSrc.mIoData.output_count*2;
+            LOGV("Outsize is now %d", outsize);
+        }
+        if (mHardware->mAudioPP.isEcnsEnabled()) {
+            // EC/NS is a blocking interface, to synchronise with read.
+            // It also consumes data when EC/NS is running.
+            // It expects MONO data.
+            // If EC/NS is not running, it will return 0, and we need to write this data to the
+            // driver ourselves.
+
+            // Indicate that it is safe to call setDriver_l() without locking mLock: if the input
+            // stream is started, doRouting_l() will not block when setDriver_l() is called.
+            mLocked = true;
+            LOGV("writeDownlinkEcns size %d", outsize);
+            written = mHardware->mAudioPP.writeDownlinkEcns(outFd,(void *)buffer,
+                                                            stereo, outsize, &mFdLock);
+            mLocked = false;
+        }
+        if (mHardware->mAudioPP.isEcnsEnabled() || mSrc.initted()) {
+            // Move audio back up to Stereo, if the EC/NS wasn't in fact running and we're
+            // writing to a stereo device.
+            if (stereo &&
+                written != (ssize_t)outsize) {
+                // Back up to stereo, in place.
+                int16_t *destBuf = (int16_t *) buffer;
+                for (int i = outsize/2-1; i >= 0; i--) {
+                    destBuf[i*2] = destBuf[i];
+                    destBuf[i*2+1] = destBuf[i];
+                }
+                outsize <<= 1;
+            }
+        }
+#endif
+
+        if (written != (ssize_t)outsize) {
+            // The sample rate conversion modifies the output size.
+            if (outsize&0x3) {
+                int16_t* bufp = (int16_t *)buffer;
+//                LOGV("Keep the spare sample away from the driver.");
+                mHaveSpareSample = true;
+                mSpareSample = bufp[outsize/2 - 1];
+            }
+
+            if (outFd >= 0) {
+                Mutex::Autolock lock2(mFdLock);
+                written = ::write(outFd, buffer, outsize&(~0x3));
+                if (written != ((ssize_t)outsize&(~0x3))) {
+                    status = written;
+                    goto error;
+                }
+            } else {
+                written = writtenToSpdif;
+            }
+        }
+        if (written < 0) {
+            LOGE("Error writing %d bytes to output: %s", outsize, strerror(errno));
+            status = written;
+            goto error;
+        }
+
+        // Sample rate converter may be stashing a couple of bytes here or there,
+        // so just report that all bytes were consumed. (it would be a bug not to.)
+        LOGV("write() written %d", bytes);
+        return bytes;
+
+    }
+error:
+    LOGE("write(): error, return %d", status);
+    standby();
+    usleep(bytes * 1000 / frameSize() / sampleRate() * 1000);
+
+    return status;
+}
+
+void AudioHardware::AudioStreamOutTegra::flush()
+{
+    // Prevent someone from writing the fd while we flush
+    Mutex::Autolock lock(mFdLock);
+    LOGD("AudioStreamOutTegra::flush()");
+    if (::ioctl(mFdCtl, TEGRA_AUDIO_OUT_FLUSH) < 0)
+       LOGE("could not flush playback: %s", strerror(errno));
+    if (::ioctl(mBtFdCtl, TEGRA_AUDIO_OUT_FLUSH) < 0)
+       LOGE("could not flush bluetooth: %s", strerror(errno));
+    if (mSpdifFdCtl >= 0 && ::ioctl(mSpdifFdCtl, TEGRA_AUDIO_OUT_FLUSH) < 0)
+       LOGE("could not flush spdif: %s", strerror(errno));
+    LOGD("AudioStreamOutTegra::flush() returns");
+}
+
+// FIXME: this is a workaround for issue 3387419 with impact on latency
+// to be removed when root cause is fixed
+void AudioHardware::AudioStreamOutTegra::setNumBufs(int numBufs)
+{
+    Mutex::Autolock lock(mFdLock);
+    LOGD("AudioStreamOutTegra::setNumBufs()");
+    if (::ioctl(mFdCtl, TEGRA_AUDIO_OUT_SET_NUM_BUFS, &numBufs) < 0)
+       LOGE("could not set number of output buffers: %s", strerror(errno));
+}
+
+// Called with mLock and mHardware->mLock held
+status_t AudioHardware::AudioStreamOutTegra::online_l()
+{
+    status_t status = NO_ERROR;
+
+    if (mState < AUDIO_STREAM_NEW_RATE_REQ) {
+        if (mState == AUDIO_STREAM_IDLE) {
+            LOGV("output %p going online", this);
+            mState = AUDIO_STREAM_CONFIG_REQ;
+            // update EC state if necessary
+            AudioStreamInTegra *input = mHardware->getActiveInput_l();
+            if (mHardware->isInCall() &&
+                input && input->source() == AUDIO_SOURCE_VOICE_COMMUNICATION) {
+                // doRouting_l() will not try to lock mLock when calling setDriver_l()
+                mLocked = true;
+                mHardware->doRouting_l();
+                mLocked = false;
+            }
+        }
+
+        // If there's no hardware speaker, leave the HW alone. (i.e. SCO/SPDIF is on)
+        if (mIsSpkrEnabledReq) {
+            status = mHardware->doStandby(mFdCtl, true, false); // output, online
+        } else {
+            status = mHardware->doStandby(mFdCtl, true, true); // output, standby
+        }
+        mIsSpkrEnabled = mIsSpkrEnabledReq;
+
+        if ((mIsBtEnabled && !mIsBtEnabledReq) ||
+            (mIsSpdifEnabled && !mIsSpdifEnabledReq)) {
+            flush();
+        }
+        mIsBtEnabled = mIsBtEnabledReq;
+        mIsSpdifEnabled = mIsSpdifEnabledReq;
+
+        int bit_format = TEGRA_AUDIO_BIT_FORMAT_DEFAULT;
+        bool is_bt_bypass = false;
+        if (mIsBtEnabled) {
+            bit_format = TEGRA_AUDIO_BIT_FORMAT_DSP;
+            is_bt_bypass = true;
+        }
+        // Setup the I2S2-> DAP2/4 capture/playback path.
+        if (::ioctl(mBtFdIoCtl, TEGRA_AUDIO_SET_BIT_FORMAT, &bit_format) < 0) {
+            LOGE("could not set bit format %s", strerror(errno));
+        }
+        if (::ioctl(mHardware->mCpcapCtlFd, CPCAP_AUDIO_SET_BLUETOOTH_BYPASS, is_bt_bypass) < 0) {
+            LOGE("could not set bluetooth bypass %s", strerror(errno));
+        }
+
+    }
+
+    mDriverRate = mHardware->mHwOutRate;
+
+    mState = AUDIO_STREAM_CONFIGURED;
+
+    return status;
+}
+
+status_t AudioHardware::AudioStreamOutTegra::standby()
+{
+    if (!mHardware) {
+        return NO_INIT;
+    }
+
+    status_t status = NO_ERROR;
+    Mutex::Autolock lock(mHardware->mLock);
+    Mutex::Autolock lock2(mLock);
+
+    if (mState != AUDIO_STREAM_IDLE) {
+        LOGV("output %p going into standby", this);
+        mState = AUDIO_STREAM_IDLE;
+
+        // update EC state if necessary
+        AudioStreamInTegra *input = mHardware->getActiveInput_l();
+        if (mHardware->isInCall() &&
+            input && input->source() == AUDIO_SOURCE_VOICE_COMMUNICATION) {
+            // doRouting_l will not try to lock mLock when calling setDriver_l()
+            mLocked = true;
+            mHardware->doRouting_l();
+            mLocked = false;
+        }
+
+#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
+    // Prevent EC/NS from writing to the file anymore.
+        mHardware->mAudioPP.writeDownlinkEcns(-1,0,false,0,&mFdLock);
+#endif
+        if (mIsSpkrEnabled) {
+            // doStandby() calls flush() which also handles the case where multiple devices
+            // including bluetooth or SPDIF are selected
+            status = mHardware->doStandby(mFdCtl, true, true); // output, standby
+        } else if (mIsBtEnabled || mIsSpdifEnabled) {
+            flush();
+        }
+    }
+
+    return status;
+}
+
+status_t AudioHardware::AudioStreamOutTegra::dump(int fd, const Vector<String16>& args)
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+    result.append("AudioStreamOutTegra::dump\n");
+    snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\tformat: %d\n", format());
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\tmHardware: %p\n", mHardware);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\tmFd: %d\n", mFd);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\tmStartCount: %d\n", mStartCount);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\tmRetryCount: %d\n", mRetryCount);
+    result.append(buffer);
+    if (mHardware)
+        snprintf(buffer, SIZE, "\tmStandby: %s\n",
+                 mHardware->mCurOutDevice.on ? "false": "true");
+    else
+        snprintf(buffer, SIZE, "\tmStandby: unknown\n");
+
+    result.append(buffer);
+    ::write(fd, result.string(), result.size());
+    return NO_ERROR;
+}
+
+bool AudioHardware::AudioStreamOutTegra::getStandby()
+{
+    return mState == AUDIO_STREAM_IDLE;;
+}
+
+status_t AudioHardware::AudioStreamOutTegra::setParameters(const String8& keyValuePairs)
+{
+    AudioParameter param = AudioParameter(keyValuePairs);
+    String8 key = String8(AudioParameter::keyRouting);
+    status_t status = NO_ERROR;
+    int device;
+    LOGV("AudioStreamOutTegra::setParameters() %s", keyValuePairs.string());
+
+    if (param.getInt(key, device) == NO_ERROR) {
+        if (device != 0) {
+            mDevices = device;
+            LOGV("set output routing %x", mDevices);
+            status = mHardware->doRouting();
+        }
+        param.remove(key);
+    }
+
+    if (param.size()) {
+        status = BAD_VALUE;
+    }
+    return status;
+}
+
+String8 AudioHardware::AudioStreamOutTegra::getParameters(const String8& keys)
+{
+    AudioParameter param = AudioParameter(keys);
+    String8 value;
+    String8 key = String8(AudioParameter::keyRouting);
+
+    if (param.get(key, value) == NO_ERROR) {
+        LOGV("get routing %x", mDevices);
+        param.addInt(key, (int)mDevices);
+    }
+
+    LOGV("AudioStreamOutTegra::getParameters() %s", param.toString().string());
+    return param.toString();
+}
+
+status_t AudioHardware::AudioStreamOutTegra::getRenderPosition(uint32_t *dspFrames)
+{
+    //TODO: enable when supported by driver
+    return INVALID_OPERATION;
+}
+
+// ----------------------------------------------------------------------------
+
+// always succeeds, must call set() immediately after
+AudioHardware::AudioStreamInTegra::AudioStreamInTegra() :
+    mHardware(0), mFd(-1), mFdCtl(-1), mState(AUDIO_STREAM_IDLE), mRetryCount(0),
+    mFormat(AUDIO_HW_IN_FORMAT), mChannels(AUDIO_HW_IN_CHANNELS),
+    mSampleRate(AUDIO_HW_IN_SAMPLERATE), mBufferSize(AUDIO_HW_IN_BUFFERSIZE),
+    mAcoustics((AudioSystem::audio_in_acoustics)0), mDevices(0),
+    mIsMicEnabled(0), mIsBtEnabled(0),
+    mSource(AUDIO_SOURCE_DEFAULT), mLocked(false), mTotalBuffersRead(0),
+    mDriverRate(AUDIO_HW_IN_SAMPLERATE)
+{
+    LOGV("AudioStreamInTegra constructor");
+}
+
+// serves a similar purpose as init()
+status_t AudioHardware::AudioStreamInTegra::set(
+        AudioHardware* hw, uint32_t devices, int *pFormat, uint32_t *pChannels, uint32_t *pRate,
+        AudioSystem::audio_in_acoustics acoustic_flags)
+{
+    Mutex::Autolock lock(mLock);
+    status_t status = BAD_VALUE;
+    mHardware = hw;
+    if (pFormat == 0)
+        return status;
+    if (*pFormat != AUDIO_HW_IN_FORMAT) {
+        LOGE("wrong in format %d, expecting %lld", *pFormat, AUDIO_HW_IN_FORMAT);
+        *pFormat = AUDIO_HW_IN_FORMAT;
+        return status;
+    }
+
+    if (pRate == 0)
+        return status;
+
+    uint32_t rate = hw->getInputSampleRate(*pRate);
+    if (rate != *pRate) {
+        LOGE("wrong sample rate %d, expecting %d", *pRate, rate);
+        *pRate = rate;
+        return status;
+    }
+
+    if (pChannels == 0)
+        return status;
+
+    if (*pChannels != AudioSystem::CHANNEL_IN_MONO &&
+        *pChannels != AudioSystem::CHANNEL_IN_STEREO) {
+        LOGE("wrong number of channels %d", *pChannels);
+        *pChannels = AUDIO_HW_IN_CHANNELS;
+        return status;
+    }
+
+    LOGV("AudioStreamInTegra::set(%d, %d, %u)", *pFormat, *pChannels, *pRate);
+
+    mDevices = devices;
+    mFormat = AUDIO_HW_IN_FORMAT;
+    mChannels = *pChannels;
+    mSampleRate = *pRate;
+    mBufferSize = mHardware->getInputBufferSize(mSampleRate, AudioSystem::PCM_16_BIT,
+                                                AudioSystem::popCount(mChannels));
+    return NO_ERROR;
+}
+
+AudioHardware::AudioStreamInTegra::~AudioStreamInTegra()
+{
+    LOGV("AudioStreamInTegra destructor");
+
+    standby();
+
+    if (mFd >= 0) {
+        ::close(mFd);
+        mFd = -1;
+    }
+
+    if (mFdCtl >= 0) {
+        ::close(mFdCtl);
+        mFdCtl = -1;
+    }
+}
+
+// Called with mHardware->mLock and mLock held.
+void AudioHardware::AudioStreamInTegra::setDriver_l(bool mic, bool bluetooth, int sampleRate)
+{
+    LOGD("%s: Analog mic? %s. Bluetooth? %s.", __FUNCTION__,
+            mic?"yes":"no", bluetooth?"yes":"no");
+
+    // force some reconfiguration at next read()
+    // Note: mState always == AUDIO_STREAM_CONFIGURED when setDriver_l() is called on an input
+    if (mic != mIsMicEnabled || bluetooth != mIsBtEnabled) {
+        mState = AUDIO_STREAM_CONFIG_REQ;
+    } else if (sampleRate != mDriverRate) {
+        mState = AUDIO_STREAM_NEW_RATE_REQ;
+    }
+
+    mIsMicEnabled = mic;
+    mIsBtEnabled = bluetooth;
+
+}
+
+ssize_t AudioHardware::AudioStreamInTegra::read(void* buffer, ssize_t bytes)
+{
+    status_t status;
+    if (!mHardware) {
+        LOGE("%s: mHardware is null", __FUNCTION__);
+        return NO_INIT;
+    }
+    // LOGV("AudioStreamInTegra::read(%p, %ld) TID %d", buffer, bytes, gettid());
+
+    bool needsOnline = false;
+    if (mState < AUDIO_STREAM_CONFIGURED) {
+        mHardware->mLock.lock();
+        if (mState < AUDIO_STREAM_CONFIGURED) {
+            needsOnline = true;
+        } else {
+            mHardware->mLock.unlock();
+        }
+    }
+
+    {   // scope for mLock
+        Mutex::Autolock lock(mLock);
+
+        ssize_t ret;
+        bool srcReqd;
+        int  hwReadBytes;
+        int16_t * inbuf;
+
+        if (needsOnline) {
+            status = online_l();
+            mHardware->mLock.unlock();
+            if (status != NO_ERROR) {
+               LOGE("%s: Problem switching to online.",__FUNCTION__);
+               goto error;
+            }
+        }
+
+        srcReqd = (mDriverRate != (int)mSampleRate);
+
+#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
+        if (srcReqd) {
+            hwReadBytes = ( bytes*mDriverRate/mSampleRate ) & (~0x7);
+            LOGV("Running capture SRC.  HW=%d bytes at %d, Flinger=%d bytes at %d",
+                  hwReadBytes, mDriverRate, (int)bytes, mSampleRate);
+            inbuf = mInScratch;
+            if ((size_t)bytes > sizeof(mInScratch)) {
+                LOGE("read: buf size problem. %d>%d",(int)bytes,sizeof(mInScratch));
+                status = BAD_VALUE;
+                goto error;
+            }
+            // Check if we need to init the rate converter
+            if (!mSrc.initted() ||
+                 mSrc.inRate() != mDriverRate ||
+                 mSrc.outRate() != (int)mSampleRate) {
+                LOGI ("%s: Upconvert started from %d to %d", __FUNCTION__,
+                       mDriverRate, mSampleRate);
+                mSrc.init(mDriverRate, mSampleRate);
+                if (!mSrc.initted()) {
+                    status = NO_INIT;
+                    goto error;
+                }
+                reopenReconfigDriver();
+            }
+        } else {
+            hwReadBytes = bytes;
+            inbuf = (int16_t *)buffer;
+            mSrc.deinit();
+        }
+        // Read from driver, or ECNS thread, as appropriate.
+        ret = mHardware->mAudioPP.read(mFd, inbuf, hwReadBytes, mDriverRate);
+        if (ret>0 && srcReqd) {
+            mSrc.mIoData.in_buf_ch1 = (SRC16 *) (inbuf);
+            mSrc.mIoData.in_buf_ch2 = 0;
+            mSrc.mIoData.input_count = hwReadBytes / sizeof(SRC16);
+            mSrc.mIoData.out_buf_ch1 = (SRC16 *) (buffer);
+            mSrc.mIoData.out_buf_ch2 = 0;
+            mSrc.mIoData.output_count = bytes/sizeof(SRC16);
+            mSrc.srcConvert();
+            ret = mSrc.mIoData.output_count*sizeof(SRC16);
+            if (ret > bytes) {
+                LOGE("read: buffer overrun");
+            }
+        }
+#else
+        if (srcReqd) {
+            LOGE("%s: sample rate mismatch HAL %d, driver %d",
+                 __FUNCTION__, mSampleRate, mDriverRate);
+            status = INVALID_OPERATION;
+            goto error;
+        }
+        ret = ::read(mFd, buffer, hwReadBytes);
+#endif
+
+        // It is not optimal to mute after all the above processing but it is necessary to
+        // keep the clock sync from input device. It also avoids glitches on output streams due
+        // to EC being turned on and off
+        bool muted;
+        mHardware->getMicMute(&muted);
+        if (muted) {
+            LOGV("%s muted",__FUNCTION__);
+            memset(buffer, 0, bytes);
+        }
+
+        LOGV("%s returns %d.",__FUNCTION__, (int)ret);
+        if (ret < 0) {
+            status = ret;
+            goto error;
+        }
+
+        mTotalBuffersRead++;
+        return ret;
+    }
+
+error:
+    LOGE("read(): error, return %d", status);
+    standby();
+    usleep(bytes * 1000 / frameSize() / sampleRate() * 1000);
+    return status;
+}
+
+bool AudioHardware::AudioStreamInTegra::getStandby() const
+{
+    return mState == AUDIO_STREAM_IDLE;
+}
+
+status_t AudioHardware::AudioStreamInTegra::standby()
+{
+    if (!mHardware) {
+        return NO_INIT;
+    }
+
+    Mutex::Autolock lock(mHardware->mLock);
+    Mutex::Autolock lock2(mLock);
+    status_t status = NO_ERROR;
+    if (mState != AUDIO_STREAM_IDLE) {
+        LOGV("input %p going into standby", this);
+        mState = AUDIO_STREAM_IDLE;
+        // setDriver_l() will not try to lock mLock when called by doRouting_l()
+        mLocked = true;
+        mHardware->doRouting_l();
+        mLocked = false;
+        status = mHardware->doStandby(mFdCtl, false, true); // input, standby
+        if (mFd >= 0) {
+            ::close(mFd);
+            mFd = -1;
+        }
+        if (mFdCtl >= 0) {
+            ::close(mFdCtl);
+            mFdCtl = -1;
+        }
+    }
+
+    return status;
+}
+
+// Called with mLock and mHardware->mLock held
+status_t AudioHardware::AudioStreamInTegra::online_l()
+{
+    status_t status = NO_ERROR;
+
+    if (mState < AUDIO_STREAM_NEW_RATE_REQ) {
+
+        reopenReconfigDriver();
+
+        // configuration
+        struct tegra_audio_in_config config;
+        status = ::ioctl(mFdCtl, TEGRA_AUDIO_IN_GET_CONFIG, &config);
+        if (status < 0) {
+            LOGE("cannot read input config: %s", strerror(errno));
+            return status;
+        }
+        config.stereo = AudioSystem::popCount(mChannels) == 2;
+        config.rate = mSampleRate;
+        status = ::ioctl(mFdCtl, TEGRA_AUDIO_IN_SET_CONFIG, &config);
+
+        if (status < 0) {
+            LOGE("cannot set input config: %s", strerror(errno));
+            if (::ioctl(mFdCtl, TEGRA_AUDIO_IN_GET_CONFIG, &config) == 0) {
+                if (config.stereo) {
+                    mChannels = AudioSystem::CHANNEL_IN_STEREO;
+                } else {
+                    mChannels = AudioSystem::CHANNEL_IN_MONO;
+                }
+            }
+        }
+
+        // Use standby to flush the driver.  mHardware->mLock should already be held
+
+        mHardware->doStandby(mFdCtl, false, true);
+        if (mDevices & ~AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
+            status = mHardware->doStandby(mFdCtl, false, false);
+        }
+
+        if (mState == AUDIO_STREAM_IDLE) {
+            mState = AUDIO_STREAM_CONFIG_REQ;
+            LOGV("input %p going online", this);
+            // setDriver_l() will not try to lock mLock when called by doRouting_l()
+            mLocked = true;
+            mHardware->doRouting_l();
+            mLocked = false;
+            mTotalBuffersRead = 0;
+            mStartTimeNs = systemTime();
+        }
+    }
+
+    mDriverRate = mHardware->mHwInRate;
+
+    mState = AUDIO_STREAM_CONFIGURED;
+
+    return status;
+}
+
+// serves a similar purpose as the init() method of other classes
+void AudioHardware::AudioStreamInTegra::reopenReconfigDriver()
+{
+    // Need to "restart" the driver when changing the buffer configuration.
+    if (mFdCtl >= 0 && ::ioctl(mFdCtl, TEGRA_AUDIO_IN_STOP) < 0) {
+        LOGE("%s: could not stop recording: %s", __FUNCTION__, strerror(errno));
+    }
+    if (mFd >= 0) {
+        ::close(mFd);
+        mFd = -1;
+    }
+    if (mFdCtl >= 0) {
+        ::close(mFdCtl);
+        mFdCtl = -1;
+    }
+
+    // This does not have a retry loop to avoid blocking if another record session already in progress
+    mFd = ::open("/dev/audio1_in", O_RDWR);
+    if (mFd < 0) {
+        LOGE("open /dev/audio1_in failed: %s", strerror(errno));
+    }
+    mFdCtl = ::open("/dev/audio1_in_ctl", O_RDWR);
+    if (mFdCtl < 0) {
+        LOGE("open /dev/audio1_in_ctl failed: %s", strerror(errno));
+        if (mFd >= 0) {
+            ::close(mFd);
+            mFd = -1;
+        }
+    } else {
+        // here we would set mInit = true;
+    }
+}
+
+status_t AudioHardware::AudioStreamInTegra::dump(int fd, const Vector<String16>& args)
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+    result.append("AudioStreamInTegra::dump\n");
+    snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\tformat: %d\n", format());
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\tmHardware: %p\n", mHardware);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\tmFd count: %d\n", mFd);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\tmState: %d\n", mState);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "\tmRetryCount: %d\n", mRetryCount);
+    result.append(buffer);
+    ::write(fd, result.string(), result.size());
+    return NO_ERROR;
+}
+
+status_t AudioHardware::AudioStreamInTegra::setParameters(const String8& keyValuePairs)
+{
+    AudioParameter param = AudioParameter(keyValuePairs);
+    String8 key = String8(AudioParameter::keyRouting);
+    status_t status = NO_ERROR;
+    int device;
+    int source;
+    LOGV("AudioStreamInTegra::setParameters() %s", keyValuePairs.string());
+
+    // read source before device so that it is upto date when doRouting() is called
+    if (param.getInt(String8(AudioParameter::keyInputSource), source) == NO_ERROR) {
+        mSource = source;
+        param.remove(String8(AudioParameter::keyInputSource));
+    }
+
+    if (param.getInt(key, device) == NO_ERROR) {
+        LOGV("set input routing %x", device);
+        if (device & (device - 1)) {
+            status = BAD_VALUE;
+        } else {
+            mDevices = device;
+            if (!getStandby() && device != 0) {
+                status = mHardware->doRouting();
+            }
+        }
+        param.remove(key);
+    }
+
+    if (param.size()) {
+        status = BAD_VALUE;
+    }
+    return status;
+}
+
+String8 AudioHardware::AudioStreamInTegra::getParameters(const String8& keys)
+{
+    AudioParameter param = AudioParameter(keys);
+    String8 value;
+    String8 key = String8(AudioParameter::keyRouting);
+
+    if (param.get(key, value) == NO_ERROR) {
+        LOGV("get routing %x", mDevices);
+        param.addInt(key, (int)mDevices);
+    }
+
+    LOGV("AudioStreamInTegra::getParameters() %s", param.toString().string());
+    return param.toString();
+}
+
+unsigned int  AudioHardware::AudioStreamInTegra::getInputFramesLost() const
+{
+    Mutex::Autolock _l(mLock);
+    unsigned int lostFrames = 0;
+    if (!getStandby()) {
+        unsigned int framesPerBuffer = bufferSize() / frameSize();
+        uint64_t expectedFrames = ((systemTime() - mStartTimeNs) * mSampleRate) / 1000000000;
+        expectedFrames = (expectedFrames / framesPerBuffer) * framesPerBuffer;
+        uint64_t actualFrames = (uint64_t)mTotalBuffersRead * framesPerBuffer;
+        if (expectedFrames > actualFrames) {
+            lostFrames = (unsigned int)(expectedFrames - actualFrames);
+            LOGW("getInputFramesLost() expected %d actual %d lost %d",
+                 (unsigned int)expectedFrames, (unsigned int)actualFrames, lostFrames);
+        }
+    }
+
+    mTotalBuffersRead = 0;
+    mStartTimeNs = systemTime();
+
+    return lostFrames;
+}
+
+// ----------------------------------------------------------------------------
+
+extern "C" AudioHardwareInterface* createAudioHardware(void) {
+    AudioHardware *hw = new AudioHardware();
+    for (unsigned tries = 0; tries < MAX_INIT_TRIES; ++tries) {
+        if (NO_ERROR == hw->init())
+            break;
+        LOGW("AudioHardware::init failed soft, retrying");
+        sleep(1);
+    }
+    if (NO_ERROR != hw->initCheck()) {
+        LOGE("AudioHardware::init failed hard");
+        delete hw;
+        hw = NULL;
+    }
+    return hw;
+}
+
+}; // namespace android
diff --git a/libaudio/AudioHardware.h b/libaudio/AudioHardware.h
new file mode 100644
index 0000000..6882832
--- /dev/null
+++ b/libaudio/AudioHardware.h
@@ -0,0 +1,341 @@
+/*
+** Copyright 2008-2010, 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 ANDROID_AUDIO_HARDWARE_H
+#define ANDROID_AUDIO_HARDWARE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/threads.h>
+#include <utils/SortedVector.h>
+
+#include <hardware_legacy/AudioHardwareBase.h>
+#include <media/mediarecorder.h>
+#include "AudioPostProcessor.h"
+#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
+extern "C" {
+#include "rate_conv.h"
+}
+#endif
+
+namespace android {
+
+#include <linux/cpcap_audio.h>
+#include <linux/tegra_audio.h>
+
+#define AUDIO_HW_OUT_SAMPLERATE 44100
+#define AUDIO_HW_NUM_OUT_BUF 2
+#define AUDIO_HW_OUT_LATENCY_MS 0
+
+// FIXME: this is a workaround for issue 3387419 with impact on latency
+// to be removed when root cause is fixed
+#define AUDIO_HW_NUM_OUT_BUF_LONG 4
+
+#define AUDIO_HW_IN_SAMPLERATE 11025                  // Default audio input sample rate
+#define AUDIO_HW_IN_CHANNELS (AudioSystem::CHANNEL_IN_MONO) // Default audio input channel mask
+#define AUDIO_HW_IN_BUFFERSIZE (4096)               // Default audio input buffer size
+#define AUDIO_HW_IN_FORMAT (AudioSystem::PCM_16_BIT)  // Default audio input sample format
+
+enum {
+    AUDIO_HW_GAIN_SPKR_GAIN = 0,
+    AUDIO_HW_GAIN_MIC_GAIN,
+    AUDIO_HW_GAIN_NUM_DIRECTIONS
+};
+enum {
+    AUDIO_HW_GAIN_USECASE_VOICE= 0,
+    AUDIO_HW_GAIN_USECASE_MM,
+    AUDIO_HW_GAIN_USECASE_VOICE_REC,
+    AUDIO_HW_GAIN_NUM_USECASES
+};
+enum {
+    AUDIO_HW_GAIN_EARPIECE = 0,
+    AUDIO_HW_GAIN_SPEAKERPHONE,
+    AUDIO_HW_GAIN_HEADSET_W_MIC,
+    AUDIO_HW_GAIN_MONO_HEADSET,
+    AUDIO_HW_GAIN_HEADSET_NO_MIC,
+    AUDIO_HW_GAIN_EMU_DEVICE,
+    AUDIO_HW_GAIN_RSVD1,
+    AUDIO_HW_GAIN_RSVD2,
+    AUDIO_HW_GAIN_RSVD3,
+    AUDIO_HW_GAIN_RSVD4,
+    AUDIO_HW_GAIN_RSVD5,
+    AUDIO_HW_GAIN_NUM_PATHS
+};
+
+enum input_state {
+    AUDIO_STREAM_IDLE,
+    AUDIO_STREAM_CONFIG_REQ,
+    AUDIO_STREAM_NEW_RATE_REQ,
+    AUDIO_STREAM_CONFIGURED
+};
+
+class AudioHardware : public  AudioHardwareBase
+{
+    class AudioStreamOutTegra;
+    class AudioStreamInTegra;
+    class AudioStreamSrc;
+
+public:
+    // AudioHardwareInterface
+                        AudioHardware();
+    virtual             ~AudioHardware();
+    virtual status_t    initCheck();
+
+    virtual status_t    setVoiceVolume(float volume);
+    virtual status_t    setMasterVolume(float volume);
+
+    virtual status_t    setMode(int mode);
+
+    // mic mute
+    virtual status_t    setMicMute(bool state);
+    virtual status_t    getMicMute(bool* state);
+
+    virtual status_t    setParameters(const String8& keyValuePairs);
+    virtual String8     getParameters(const String8& keys);
+
+    // create I/O streams
+    virtual AudioStreamOut* openOutputStream(
+                                uint32_t devices,
+                                int *format=0,
+                                uint32_t *channels=0,
+                                uint32_t *sampleRate=0,
+                                status_t *status=0);
+
+    virtual AudioStreamIn* openInputStream(
+
+                                uint32_t devices,
+                                int *format,
+                                uint32_t *channels,
+                                uint32_t *sampleRate,
+                                status_t *status,
+                                AudioSystem::audio_in_acoustics acoustics);
+
+    virtual    void        closeOutputStream(AudioStreamOut* out);
+    virtual    void        closeInputStream(AudioStreamIn* in);
+
+    virtual    size_t      getInputBufferSize(uint32_t sampleRate, int format, int channelCount);
+    // AudioHardwareBase provides default implementation
+    //virtual  status_t    dumpState(int fd, const Vector<String16>& args);
+
+    // added by AudioHardware
+               status_t    init();
+
+protected:
+    // AudioHardwareBase provides default implementation
+    //virtual  bool        isModeInCall(int mode);
+    //virtual  bool        isInCall();
+
+    // AudioHardwareInterface
+    virtual    status_t    dump(int fd, const Vector<String16>& args);
+
+    // added by AudioHardware
+                int        getActiveInputRate();
+
+private:
+
+    status_t    dumpInternals(int fd, const Vector<String16>& args);
+    uint32_t    getInputSampleRate(uint32_t sampleRate);
+    status_t    doStandby(int stop_fd, bool output, bool enable);
+    status_t    doRouting_l();
+    status_t    doRouting();
+    status_t    setVolume_l(float v, int usecase);
+    uint8_t     getGain(int direction, int usecase);
+    void        readHwGainFile();
+
+    AudioStreamInTegra*   getActiveInput_l();
+    status_t    setMicMute_l(bool state);
+
+#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
+    class AudioStreamSrc {
+    public:
+                            AudioStreamSrc();
+                            ~AudioStreamSrc();
+    inline      int         inRate() {return mSrcInRate;};
+    inline      int         outRate() {return mSrcOutRate;};
+    inline      bool        initted() {return mSrcInitted;};
+                void        init(int inRate, int outRate);
+    inline      void        deinit() {mSrcInitted = false;};
+                SRC_IO_T    mIoData;
+    inline      void        srcConvert() { rate_convert(&mIoData, &mSrcObj, 0x0800); };
+    private:
+                SRC_OBJ_T   mSrcObj;
+                char *      mSrcBuffer;
+                SRC_INIT_T  mSrcInit;
+                int         mSrcInRate;
+                int         mSrcOutRate;
+                bool        mSrcInitted;
+    };
+#endif
+
+    class AudioStreamOutTegra : public AudioStreamOut {
+    public:
+                            AudioStreamOutTegra();
+        virtual             ~AudioStreamOutTegra();
+                status_t    init();
+                status_t    initCheck();
+                void        setDriver_l(bool speaker, bool bluetooth, bool spdif, int sampleRate);
+                status_t    set(AudioHardware* mHardware,
+                                uint32_t devices,
+                                int *pFormat,
+                                uint32_t *pChannels,
+                                uint32_t *pRate);
+        virtual uint32_t    sampleRate() const { return AUDIO_HW_OUT_SAMPLERATE; }
+        // must be 32-bit aligned - driver only seems to like 4800
+        virtual size_t      bufferSize() const { return 4096; }
+        virtual uint32_t    channels() const { return AudioSystem::CHANNEL_OUT_STEREO; }
+        virtual int         format() const { return AudioSystem::PCM_16_BIT; }
+        virtual uint32_t    latency() const { return (1000*AUDIO_HW_NUM_OUT_BUF*(bufferSize()/frameSize()))/sampleRate()+AUDIO_HW_OUT_LATENCY_MS; }
+        virtual status_t    setVolume(float left, float right) { return INVALID_OPERATION; }
+        virtual ssize_t     write(const void* buffer, size_t bytes);
+                void        flush();
+        virtual status_t    standby();
+                status_t    online_l();
+        virtual status_t    dump(int fd, const Vector<String16>& args);
+                bool        getStandby();
+        virtual status_t    setParameters(const String8& keyValuePairs);
+        virtual String8     getParameters(const String8& keys);
+                uint32_t    devices() { return mDevices; }
+        virtual status_t    getRenderPosition(uint32_t *dspFrames);
+                void        lock() { mLock.lock(); }
+                void        unlock() { mLock.unlock(); }
+                bool        isLocked() { return mLocked; }
+                void        setNumBufs(int numBufs);
+
+    private:
+                AudioHardware* mHardware;
+                Mutex       mLock;
+                int         mFd;
+                int         mFdCtl;
+                int         mBtFd;
+                int         mBtFdCtl;
+                int         mBtFdIoCtl;
+                int         mSpdifFd;
+                int         mSpdifFdCtl;
+                int         mStartCount;
+                int         mRetryCount;
+                uint32_t    mDevices;
+                Mutex       mFdLock;
+                bool        mIsSpkrEnabled;
+                bool        mIsBtEnabled;
+                bool        mIsSpdifEnabled;
+                bool        mIsSpkrEnabledReq;
+                bool        mIsBtEnabledReq;
+                bool        mIsSpdifEnabledReq;
+                int16_t     mSpareSample;
+                bool        mHaveSpareSample;
+                int         mState;
+#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
+                AudioStreamSrc mSrc;
+#endif
+                bool        mLocked;        // setDriver() doesn't have to lock if true
+                int         mDriverRate;
+                bool        mInit;
+    };
+
+    class AudioStreamInTegra : public AudioStreamIn {
+    public:
+                            AudioStreamInTegra();
+        virtual             ~AudioStreamInTegra();
+                status_t    set(AudioHardware* mHardware,
+                                uint32_t devices,
+                                int *pFormat,
+                                uint32_t *pChannels,
+                                uint32_t *pRate,
+                                AudioSystem::audio_in_acoustics acoustics);
+        virtual size_t      bufferSize() const { return mBufferSize; }
+        virtual uint32_t    channels() const { return mChannels; }
+        virtual int         format() const { return mFormat; }
+        virtual uint32_t    sampleRate() const { return mSampleRate; }
+        virtual status_t    setGain(float gain) { return INVALID_OPERATION; }
+        virtual ssize_t     read(void* buffer, ssize_t bytes);
+        virtual status_t    dump(int fd, const Vector<String16>& args);
+        virtual status_t    standby();
+        virtual status_t    online_l();
+                bool        getStandby() const;
+        virtual status_t    setParameters(const String8& keyValuePairs);
+        virtual String8     getParameters(const String8& keys);
+        virtual unsigned int  getInputFramesLost() const;
+                uint32_t    devices() { return mDevices; }
+                void        setDriver_l(bool mic, bool bluetooth, int sampleRate);
+                int         source() const { return mSource; }
+                void        lock() { mLock.lock(); }
+                void        unlock() { mLock.unlock(); }
+                bool        isLocked() { return mLocked; }
+
+    private:
+                void        reopenReconfigDriver();
+
+                AudioHardware* mHardware;
+        mutable Mutex       mLock;
+                int         mFd;
+                int         mFdCtl;
+                int         mState;
+                int         mRetryCount;
+                int         mFormat;
+                uint32_t    mChannels;
+                uint32_t    mSampleRate;
+                size_t      mBufferSize;
+                AudioSystem::audio_in_acoustics mAcoustics;
+                uint32_t    mDevices;
+                bool        mIsMicEnabled;
+                bool        mIsBtEnabled;
+                int         mSource;
+                // 20 millisecond scratch buffer
+                int16_t     mInScratch[48000/50];
+#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
+                AudioStreamSrc mSrc;
+#endif
+                bool        mLocked;        // setDriver() doesn't have to lock if true
+        mutable uint32_t    mTotalBuffersRead;
+        mutable nsecs_t     mStartTimeNs;
+                int         mDriverRate;
+    };
+
+            static const uint32_t inputSamplingRates[];
+            bool        mInit;
+            bool        mMicMute;
+            bool        mBluetoothNrec;
+            uint32_t    mBluetoothId;
+            AudioStreamOutTegra*  mOutput;
+            SortedVector <AudioStreamInTegra*>   mInputs;
+
+            struct cpcap_audio_stream mCurOutDevice;
+            struct cpcap_audio_stream mCurInDevice;
+
+     friend class AudioStreamInTegra;
+            Mutex       mLock;
+
+            int mCpcapCtlFd;
+            int mHwOutRate;
+            int mHwInRate;
+            float mMasterVol;
+            float mVoiceVol;
+            uint8_t mCpcapGain[AUDIO_HW_GAIN_NUM_DIRECTIONS]
+                              [AUDIO_HW_GAIN_NUM_USECASES]
+                              [AUDIO_HW_GAIN_NUM_PATHS];
+#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
+            AudioPostProcessor mAudioPP;
+#endif
+            int mSpkrVolume;
+            int mMicVolume;
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_AUDIO_HARDWARE_H
diff --git a/libaudio/AudioPolicyManager.cpp b/libaudio/AudioPolicyManager.cpp
new file mode 100644
index 0000000..42887f4
--- /dev/null
+++ b/libaudio/AudioPolicyManager.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#define LOG_TAG "AudioPolicyManager"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+#include "AudioPolicyManager.h"
+#include <media/mediarecorder.h>
+
+namespace android {
+
+
+
+// ----------------------------------------------------------------------------
+// Common audio policy manager code is implemented in AudioPolicyManagerBase class
+// ----------------------------------------------------------------------------
+
+// ---  class factory
+
+
+extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
+{
+    return new AudioPolicyManager(clientInterface);
+}
+
+extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface)
+{
+    delete interface;
+}
+
+}; // namespace android
diff --git a/libaudio/AudioPolicyManager.h b/libaudio/AudioPolicyManager.h
new file mode 100644
index 0000000..b591c1f
--- /dev/null
+++ b/libaudio/AudioPolicyManager.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/Timers.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <hardware_legacy/AudioPolicyManagerBase.h>
+
+
+namespace android {
+
+class AudioPolicyManager: public AudioPolicyManagerBase
+{
+
+public:
+                AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
+                : AudioPolicyManagerBase(clientInterface) {}
+
+        virtual ~AudioPolicyManager() {}
+
+protected:
+        // true is current platform implements a back microphone
+        virtual bool hasBackMicrophone() const { return false; }
+#ifdef WITH_A2DP
+        // true is current platform supports suplication of notifications and ringtones over A2DP output
+        virtual bool a2dpUsedForSonification() const { return true; }
+#endif
+
+};
+};
diff --git a/libaudio/AudioPostProcessor.cpp b/libaudio/AudioPostProcessor.cpp
new file mode 100644
index 0000000..9d81654
--- /dev/null
+++ b/libaudio/AudioPostProcessor.cpp
@@ -0,0 +1,755 @@
+/*
+** Copyright 2010, 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.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AudioPostProcessor"
+#include <fcntl.h>
+#include <utils/Log.h>
+#include "AudioHardware.h"
+#include "AudioPostProcessor.h"
+#include <sys/stat.h>
+#include "mot_acoustics.h"
+// hardware specific functions
+extern uint16_t HC_CTO_AUDIO_MM_PARAMETER_TABLE[];
+///////////////////////////////////
+// Some logging #defines
+#define ECNS_LOG_ENABLE_OFFSET 1 // 2nd word of the configuration buffer
+#define ECNS_LOGGING_BITS 0xBFFF // 15 possible logpoints
+
+#define MOT_LOG_DELIMITER_START  0xFEED
+#define MOT_LOG_DELIMITER_END    0xF00D
+#define BASIC_DOCK_PROP_VALUE    0
+
+#define ECNSLOGPATH "/data/ecns"
+#define DOCK_PROP_PATH "/sys/class/switch/dock/dock_prop"
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+#endif
+
+//#define DEBUG_TIMING
+#ifdef DEBUG_TIMING
+struct timeval mtv1, mtv2, mtv3, mtv4, mtv5, mtv6, mtv7, mtv8;
+#define GETTIMEOFDAY gettimeofday
+#else
+#define GETTIMEOFDAY(a,b)
+#endif
+
+namespace android {
+
+AudioPostProcessor::AudioPostProcessor() :
+    mEcnsScratchBuf(0), mLogNumPoints(0),  mEcnsDlBuf(0), mEcnsThread(0)
+{
+    LOGD("%s",__FUNCTION__);
+
+    // One-time CTO Audio configuration
+    mAudioMmEnvVar.cto_audio_mm_param_block_ptr              = HC_CTO_AUDIO_MM_PARAMETER_TABLE;
+    mAudioMmEnvVar.cto_audio_mm_pcmlogging_buffer_block_ptr  = mPcmLoggingBuf;
+    mAudioMmEnvVar.pcmlogging_buffer_block_size              = ARRAY_SIZE(mPcmLoggingBuf);
+    mAudioMmEnvVar.cto_audio_mm_runtime_param_mem_ptr        = mRuntimeParam;
+    mAudioMmEnvVar.cto_audio_mm_static_memory_block_ptr      = mStaticMem;
+    mAudioMmEnvVar.cto_audio_mm_scratch_memory_block_ptr     = mScratchMem;
+    mAudioMmEnvVar.accy = CTO_AUDIO_MM_ACCY_INVALID;
+    mAudioMmEnvVar.sample_rate = CTO_AUDIO_MM_SAMPL_44100;
+
+    mEcnsThread = new EcnsThread();
+    // Initial conditions for EC/NS
+    stopEcns();
+}
+
+AudioPostProcessor::~AudioPostProcessor()
+{
+    if (mEcnsRunning) {
+        LOGD("%s",__FUNCTION__);
+        enableEcns(false);
+    }
+}
+
+uint32_t AudioPostProcessor::convOutDevToCTO(uint32_t outDev)
+{
+    int32_t dock_prop = 0;
+    // Only loudspeaker and audio docks are currently in this table
+    switch (outDev) {
+       case CPCAP_AUDIO_OUT_SPEAKER:
+           return CTO_AUDIO_MM_ACCY_LOUDSPEAKER;
+       case CPCAP_AUDIO_OUT_ANLG_DOCK_HEADSET:
+           dock_prop = read_dock_prop(DOCK_PROP_PATH);
+           if ((dock_prop < 0) || (dock_prop == BASIC_DOCK_PROP_VALUE)) {
+               // Basic dock, or error getting the dock ID
+               return CTO_AUDIO_MM_ACCY_INVALID;
+	   }
+           else // speaker dock
+               return CTO_AUDIO_MM_ACCY_DOCK;
+       default:
+           return CTO_AUDIO_MM_ACCY_INVALID;
+    }
+}
+
+uint32_t AudioPostProcessor::convRateToCto(uint32_t rate)
+{
+    switch (rate) {
+        case 44100: // Most likely.
+            return CTO_AUDIO_MM_SAMPL_44100;
+        case 8000:
+            return CTO_AUDIO_MM_SAMPL_8000;
+        case 11025:
+            return CTO_AUDIO_MM_SAMPL_11025;
+        case 12000:
+            return CTO_AUDIO_MM_SAMPL_12000;
+        case 16000:
+            return CTO_AUDIO_MM_SAMPL_16000;
+        case 22050:
+            return CTO_AUDIO_MM_SAMPL_22050;
+        case 24000:
+            return CTO_AUDIO_MM_SAMPL_24000;
+        case 32000:
+            return CTO_AUDIO_MM_SAMPL_32000;
+        case 48000:
+            return CTO_AUDIO_MM_SAMPL_48000;
+        default:
+            return CTO_AUDIO_MM_SAMPL_44100;
+    }
+}
+
+void AudioPostProcessor::configMmAudio()
+{
+    if (mAudioMmEnvVar.accy != CTO_AUDIO_MM_ACCY_INVALID) {
+        LOGD("Configure CTO Audio MM processing");
+        // fetch the corresponding runtime audio parameter
+        api_cto_audio_mm_param_parser(&(mAudioMmEnvVar), (int16_t *)0, (int16_t *)0);
+        // Initialize algorithm static memory
+        api_cto_audio_mm_init(&(mAudioMmEnvVar), (int16_t *)0, (int16_t *)0);
+    } else {
+        LOGD("CTO Audio MM processing is disabled.");
+    }
+}
+
+void AudioPostProcessor::enableEcns(bool value)
+{
+    if (mEcnsEnabled!=value) {
+        LOGD("enableEcns(%s)",value?"true":"false");
+        mEcnsEnabled = value;
+        if (value==false) {
+            mEcnsThread->requestExitAndWait();
+            stopEcns();
+            cleanupEcns();
+        }
+    }
+}
+
+void AudioPostProcessor::setAudioDev(struct cpcap_audio_stream *outDev,
+                                     struct cpcap_audio_stream *inDev,
+                                     bool is_bt, bool is_bt_ec, bool is_spdif)
+{
+    int32_t dock_prop = 0;
+    uint32_t mm_accy = convOutDevToCTO(outDev->id);
+    Mutex::Autolock lock(mMmLock);
+
+    if (is_bt) {
+        if (is_bt_ec)
+            mEcnsMode = CTO_AUDIO_USECASE_NB_BLUETOOTH_WITH_ECNS;
+        else
+            mEcnsMode = CTO_AUDIO_USECASE_NB_BLUETOOTH_WITHOUT_ECNS;
+    } else if (is_spdif) // May need a more complex check here for HDMI vs. others
+        mEcnsMode = CTO_AUDIO_USECASE_NB_ACCY_1;
+    else if (outDev->id==CPCAP_AUDIO_OUT_HEADSET && inDev->id==CPCAP_AUDIO_IN_MIC1)
+        mEcnsMode = CTO_AUDIO_USECASE_NB_HEADSET_WITH_HANDSET_MIC;
+    else if (outDev->id==CPCAP_AUDIO_OUT_HEADSET)
+        mEcnsMode = CTO_AUDIO_USECASE_NB_HEADSET;
+    else if (outDev->id==CPCAP_AUDIO_OUT_ANLG_DOCK_HEADSET) {
+        dock_prop = read_dock_prop(DOCK_PROP_PATH);
+        if ((dock_prop < 0) || (dock_prop == BASIC_DOCK_PROP_VALUE))
+            // Basic dock, or error getting the dock ID
+            mEcnsMode = CTO_AUDIO_USECASE_NB_ACCY_1;
+        else
+            // Speaker Dock
+            mEcnsMode = CTO_AUDIO_USECASE_NB_DEDICATED_DOCK;
+    }
+    else
+        mEcnsMode = CTO_AUDIO_USECASE_NB_SPKRPHONE;
+
+    if (mEcnsEnabled) {
+        // We may need to reset the EC/NS if the output device changed.
+        stopEcns();
+    }
+
+    LOGV("setAudioDev %d", outDev->id);
+    if (mm_accy != mAudioMmEnvVar.accy) {
+        mAudioMmEnvVar.accy = mm_accy;
+        configMmAudio();
+    }
+}
+
+// Setting the HW sampling rate may require reconfiguration of audio processing.
+void AudioPostProcessor::setPlayAudioRate(int sampRate)
+{
+    uint32_t rate = convRateToCto(sampRate);
+    Mutex::Autolock lock(mMmLock);
+
+    LOGD("AudioPostProcessor::setPlayAudioRate %d", sampRate);
+    if (rate != mAudioMmEnvVar.sample_rate) {
+        mAudioMmEnvVar.sample_rate = rate;
+        configMmAudio();
+    }
+}
+
+void AudioPostProcessor::doMmProcessing(void * buffer, int numSamples)
+{
+    Mutex::Autolock lock(mMmLock);
+
+    if (mAudioMmEnvVar.accy != CTO_AUDIO_MM_ACCY_INVALID &&
+        !mEcnsEnabled) {
+        // Apply the CTO audio effects in-place.
+        mAudioMmEnvVar.frame_size = numSamples;
+        api_cto_audio_mm_main(&mAudioMmEnvVar, (int16_t *)buffer, (int16_t *)buffer);
+    }
+}
+
+int AudioPostProcessor::getEcnsRate (void)
+{
+    return mEcnsRate;
+}
+
+void AudioPostProcessor::initEcns(int rate, int bytes)
+{
+    LOGD("%s",__FUNCTION__);
+    CTO_AUDIO_USECASES_CTRL mode;
+    Mutex::Autolock lock(mEcnsBufLock);
+
+    if (rate != 8000 && rate != 16000) {
+        LOGW("Invalid rate for EC/NS, disabling");
+        mEcnsEnabled = 0;
+        mEcnsRunning = 0;
+        return;
+    }
+    mode = mEcnsMode;
+    mEcnsRate = rate;
+    if (mEcnsRate==16000) {
+       // Offset to the 16K (WB) block in the coefficients file
+       mode = CTO_AUDIO_USECASES_CTRL(mode + CTO_AUDIO_USECASE_WB_HANDSET);
+    }
+    LOGD("%s for mode %d at %d size %d",__FUNCTION__, mode, mEcnsRate, bytes);
+    mEcnsCtrl.framesize = bytes/2;
+    mEcnsCtrl.micFlag = 0; // 0- one mic.  1- dual mic. 2- three mic.
+    mEcnsCtrl.digital_mode = (rate == 8000) ? 0 : 1;  // 8K or 16K
+    mEcnsCtrl.usecase = mode;
+    mMemBlocks.staticMemory_1 = mStaticMemory_1;
+    mMemBlocks.staticMemory_2 = NULL;
+    mMemBlocks.mot_datalog = mMotDatalog;
+    mMemBlocks.gainTableMemory = mParamTable;
+
+    FILE * fp = fopen("/system/etc/voip_aud_params.bin", "r");
+    if (fp) {
+        if (fread(mParamTable, sizeof(mParamTable), 1, fp) < 1) {
+            LOGE("Cannot read VOIP parameter file.  Disabling EC/NS.");
+            fclose(fp);
+            mEcnsEnabled = 0;
+            mEcnsRunning = 0;
+            return;
+        }
+        fclose(fp);
+    }
+    else {
+        LOGE("Cannot open VOIP parameter file.  Disabling EC/NS.");
+        mEcnsEnabled = 0;
+        mEcnsRunning = 0;
+        return;
+    }
+
+    mEcnsRunning = 1;
+    mEcnsOutBuf = 0;
+    mEcnsOutBufSize = 0;
+    mEcnsOutBufReadOffset = 0;
+
+    // Send setup parameters to the EC/NS module, init the module.
+    API_MOT_SETUP(&mEcnsCtrl, &mMemBlocks);
+    API_MOT_INIT(&mEcnsCtrl, &mMemBlocks);
+}
+
+void AudioPostProcessor::stopEcns (void)
+{
+    AutoMutex lock(mEcnsBufLock);
+    if (mEcnsRunning) {
+        LOGD("%s",__FUNCTION__);
+        mEcnsRunning = 0;
+    }
+}
+
+void AudioPostProcessor::cleanupEcns(void)
+{
+    AutoMutex lock(mEcnsBufLock);
+    mEcnsRate = 0;
+    if (mEcnsScratchBuf) {
+        free(mEcnsScratchBuf);
+        mEcnsScratchBuf = 0;
+    }
+    mEcnsScratchBufSize = 0;
+    mEcnsOutFd = -1;
+
+    if (mEcnsDlBuf) {
+       free(mEcnsDlBuf);
+       mEcnsDlBuf = 0;
+    }
+    mEcnsDlBufSize = 0;
+    // In case write() is blocked, set it free.
+    mEcnsBufCond.signal();
+
+    ecnsLogToFile();
+}
+
+
+// Returns: Bytes written (actually "to-be-written" by EC/NS thread).
+int AudioPostProcessor::writeDownlinkEcns(int fd, void * buffer, bool stereo,
+                                          int bytes, Mutex *fdLock)
+{
+    int written = 0;
+    mEcnsBufLock.lock();
+    if (mEcnsEnabled && !mEcnsRunning) {
+        long usecs = 20*1000;
+        // Give the read thread a chance to catch up.
+        LOGV("%s: delay %d msecs for ec/ns to start",__FUNCTION__, (int)(usecs/1000));
+        mEcnsBufLock.unlock();
+        usleep(usecs);
+        mEcnsBufLock.lock();
+        written = bytes;  // Pretend all data was consumed even if ecns isn't running
+    }
+    if (mEcnsRunning) {
+        // Only run through here after initEcns has been done by read thread.
+        mEcnsOutFd = fd;
+        mEcnsOutBuf = buffer;
+        mEcnsOutBufSize = bytes;
+        mEcnsOutBufReadOffset = 0;
+        mEcnsOutFdLockp = fdLock;
+        mEcnsOutStereo = stereo;
+        if (mEcnsBufCond.waitRelative(mEcnsBufLock, seconds(1)) != NO_ERROR) {
+            LOGE("%s: Capture thread is stalled.", __FUNCTION__);
+        }
+        if (mEcnsOutBufSize != 0)
+            LOGD("%s: Buffer not consumed", __FUNCTION__);
+        else
+            written = bytes;  // All data consumed
+    }
+    mEcnsBufLock.unlock();
+    return written;
+}
+
+// Returns: Bytes read.
+int AudioPostProcessor::read(int fd, void * buffer, int bytes, int rate)
+{
+    if (mEcnsEnabled) {
+        return mEcnsThread->readData(fd, buffer, bytes, rate, this);
+    }
+    ssize_t ret;
+    ret = ::read(fd, buffer, bytes);
+    if (ret < 0)
+        LOGE("Error reading from audio in: %s", strerror(errno));
+    return (int)ret;
+}
+
+// Returns: Bytes processed.
+int AudioPostProcessor::applyUplinkEcns(void * buffer, int bytes, int rate)
+{
+    static int16 ul_gbuff2[160];
+    int16_t *dl_buf;
+    int16_t *ul_buf = (int16_t *)buffer;
+    int dl_buf_bytes=0;
+    // The write thread could have left us with one frame of data in the
+    // driver when we started reading.
+    static bool onetime;
+
+    if (!mEcnsEnabled)
+        return 0;
+
+    LOGV("%s %d bytes at %d Hz",__FUNCTION__, bytes, rate);
+    if (mEcnsEnabled && !mEcnsRunning) {
+        initEcns(rate, bytes);
+        onetime=true;
+    }
+
+    // In case the rate switched..
+    if (mEcnsEnabled && rate != mEcnsRate) {
+        stopEcns();
+        initEcns(rate, bytes);
+        onetime=true;
+    }
+
+    if (!mEcnsRunning) {
+        LOGE("EC/NS failed to init, read returns.");
+        return -1;
+    }
+
+    mEcnsBufLock.lock();
+    // Need a contiguous stereo playback buffer in the end.
+    if (bytes*2 != mEcnsDlBufSize || !mEcnsDlBuf) {
+        if (mEcnsDlBuf)
+            free(mEcnsDlBuf);
+        mEcnsDlBuf = (int16_t*)malloc(bytes*2);
+        if (mEcnsDlBuf)
+            mEcnsDlBufSize = bytes*2;
+    }
+    dl_buf = mEcnsDlBuf;
+    if (!dl_buf) {
+        mEcnsBufLock.unlock();
+        return -1;
+    }
+
+    // Need to gather appropriate amount of downlink speech.
+    // Take oldest scratch data first.  The scratch buffer holds fractions of buffers
+    // that were too small for processing.
+    if (mEcnsScratchBuf && mEcnsScratchBufSize) {
+        dl_buf_bytes = mEcnsScratchBufSize > bytes ? bytes:mEcnsScratchBufSize;
+        memcpy(dl_buf, mEcnsScratchBuf, dl_buf_bytes);
+        //LOGD("Took %d bytes from mEcnsScratchBuf", dl_buf_bytes);
+        mEcnsScratchBufSize -= dl_buf_bytes;
+        if (mEcnsScratchBufSize==0) {
+            // This should always be true.
+            free(mEcnsScratchBuf);
+            mEcnsScratchBuf = 0;
+            mEcnsScratchBufSize = 0;
+        }
+    }
+    // Take fresh data from write thread second.
+    if (dl_buf_bytes < bytes) {
+        int bytes_to_copy = mEcnsOutBufSize - mEcnsOutBufReadOffset;
+        bytes_to_copy = bytes_to_copy + dl_buf_bytes > bytes?
+                      bytes-dl_buf_bytes:bytes_to_copy;
+        if (bytes_to_copy) {
+            memcpy((void *)((unsigned int)dl_buf+dl_buf_bytes),
+                   (void *)((unsigned int)mEcnsOutBuf+mEcnsOutBufReadOffset),
+                   bytes_to_copy);
+            dl_buf_bytes += bytes_to_copy;
+        }
+        //LOGD("Took %d bytes from mEcnsOutBuf.  Need %d more.", bytes_to_copy,
+        //      bytes-dl_buf_bytes);
+        mEcnsOutBufReadOffset += bytes_to_copy;
+        if (mEcnsOutBufSize - mEcnsOutBufReadOffset < bytes) {
+            // We've depleted the output buffer, it's smaller than one uplink "frame".
+            // First take any unused data into scratch, then free the write thread.
+            if (mEcnsScratchBuf) {
+                LOGE("Memleak - coding error");
+                free(mEcnsScratchBuf);
+            }
+            if (mEcnsOutBufSize - mEcnsOutBufReadOffset > 0) {
+                if ((mEcnsScratchBuf=malloc(mEcnsOutBufSize - mEcnsOutBufReadOffset)) == 0) {
+                    LOGE("%s: Alloc failed, scratch data lost.",__FUNCTION__);
+                } else {
+                    mEcnsScratchBufSize = mEcnsOutBufSize - mEcnsOutBufReadOffset;
+                    //LOGD("....store %d bytes into scratch buf %p",
+                    //     mEcnsScratchBufSize, mEcnsScratchBuf);
+                    memcpy(mEcnsScratchBuf,
+                           (void *)((unsigned int)mEcnsOutBuf+mEcnsOutBufReadOffset),
+                           mEcnsScratchBufSize);
+                }
+            }
+            mEcnsOutBuf = 0;
+            mEcnsOutBufSize = 0;
+            mEcnsOutBufReadOffset = 0;
+            //LOGD("Signal write thread - need data.");
+            mEcnsBufCond.signal();
+        }
+    }
+
+    mEcnsBufLock.unlock();
+
+    // Pad downlink with zeroes as last resort.  We have to process the UL speech.
+    if (dl_buf_bytes < bytes) {
+        LOGV("%s:EC/NS Starved for downlink data. have %d need %d.",
+             __FUNCTION__,dl_buf_bytes, bytes);
+        memset(&dl_buf[dl_buf_bytes/sizeof(int16_t)],
+               0,
+               bytes-dl_buf_bytes);
+    }
+
+    // Do Echo Cancellation
+    GETTIMEOFDAY(&mtv4, NULL);
+    API_MOT_LOG_RESET(&mEcnsCtrl, &mMemBlocks);
+    API_MOT_DOWNLINK(&mEcnsCtrl, &mMemBlocks, (int16*)dl_buf, (int16*)ul_buf, &(ul_gbuff2[0]));
+    API_MOT_UPLINK(&mEcnsCtrl, &mMemBlocks, (int16*)dl_buf, (int16*)ul_buf, &(ul_gbuff2[0]));
+
+    // Playback the echo-cancelled speech to driver.
+    // Include zero padding.  Our echo canceller needs a consistent path.
+    if (mEcnsOutStereo) {
+        // Convert up to stereo, in place.
+        for (int i = bytes/2-1; i >= 0; i--) {
+            dl_buf[i*2] = dl_buf[i];
+            dl_buf[i*2+1] = dl_buf[i];
+        }
+        dl_buf_bytes *= 2;
+    }
+    GETTIMEOFDAY(&mtv5, NULL);
+    if (mEcnsOutFd != -1) {
+        mEcnsOutFdLockp->lock();
+        ::write(mEcnsOutFd, &dl_buf[0],
+                bytes*(mEcnsOutStereo?2:1));
+        mEcnsOutFdLockp->unlock();
+    }
+    // Do the CTO SuperAPI internal logging.
+    // (Do this after writing output to avoid adding latency.)
+    GETTIMEOFDAY(&mtv6, NULL);
+    ecnsLogToRam(bytes);
+    return bytes;
+}
+void AudioPostProcessor::ecnsLogToRam (int bytes)
+{
+    uint16_t *logp;
+    int mode = mEcnsMode + (mEcnsRate==16000?CTO_AUDIO_USECASE_WB_HANDSET:0);
+    uint16_t *audioProfile = &mParamTable[AUDIO_PROFILE_PARAMETER_BLOCK_WORD16_SIZE*mode];
+
+    if (audioProfile[ECNS_LOG_ENABLE_OFFSET] & ECNS_LOGGING_BITS) {
+        if (!mLogBuf[0]) {
+            mLogNumPoints = 0;
+            mLogOffset = 0;
+            LOGE("EC/NS AUDIO LOGGER CONFIGURATION:");
+            LOGE("log enable %04X",
+                audioProfile[ECNS_LOG_ENABLE_OFFSET]);
+            mkdir(ECNSLOGPATH, 00770);
+            for (uint16_t i=1; i>0; i<<=1) {
+                if (i&ECNS_LOGGING_BITS&audioProfile[ECNS_LOG_ENABLE_OFFSET]) {
+                   mLogNumPoints++;
+                }
+            }
+            LOGE("Number of log points is %d.", mLogNumPoints);
+            logp = mMotDatalog;
+            mLogSize = 10*60*50*bytes;
+            for (int i=0; i<mLogNumPoints; i++) {
+                // Allocate 10 minutes of logging per point
+                mLogBuf[i]=(char *)malloc(mLogSize);
+                if (!mLogBuf[i]) {
+                    LOGE("%s: Memory allocation failed.", __FUNCTION__);
+                    for (int j=0; j<i; j++) {
+                        free(mLogBuf[j]);
+                        mLogBuf[j]=0;
+                    }
+                    return;
+                }
+            }
+        }
+        if (mLogOffset+bytes > mLogSize)
+            return;
+        logp = mMotDatalog;
+        for (int i=0; i<mLogNumPoints; i++) {
+            if (mLogBuf[i]) {
+                mLogPoint[i] = logp[1];
+                memcpy(&mLogBuf[i][mLogOffset], &logp[4], logp[2]*sizeof(uint16_t));
+                logp += 4+logp[2];
+            } else {
+                LOGE("EC/NS logging enabled, but memory not allocated");
+            }
+        }
+        mLogOffset += bytes;
+    }
+}
+
+void AudioPostProcessor::ecnsLogToFile()
+{
+    if (mLogNumPoints && mLogOffset > 16000*2) {
+        for (int i=0; i<mLogNumPoints; i++) {
+            FILE * fp;
+            char fname[80];
+            sprintf(fname, ECNSLOGPATH"/log-0x%04X.pcm", mLogPoint[i]);
+            fp = fopen((const char *)fname, "w");
+            if (fp) {
+                LOGE("Writing %d bytes to %s", mLogOffset, fname);
+                fwrite(mLogBuf[i], mLogOffset, 1, fp);
+                fclose(fp);
+            } else {
+                LOGE("Problem writing to %s", fname);
+            }
+        }
+    }
+    mLogOffset = 0;
+}
+
+int AudioPostProcessor::read_dock_prop(char const *path)
+{
+    int fd = -1;
+    const size_t SIZE = 7;
+    static int already_warned = -1;
+    char buffer[SIZE];
+    /* the docks come with a property id AC000 for basic docks
+       and AC002 for speaker docks, numbers might change, keeping
+       them for now.
+     */
+    unsigned long int basic_dock_prop = 0xAC000;
+    unsigned long int spkr_dock_prop;
+
+    buffer[SIZE - 1] = '\0';
+    fd = open(path, O_RDONLY);
+    if (fd >= 0) {
+        int amt = ::read(fd, buffer, SIZE-1);
+        if (amt != SIZE-1) {
+	    LOGE("Incomplete dock property read, cannot validate dock");
+	    return -1;
+        }
+        spkr_dock_prop = strtoul(buffer, NULL, 16);
+	if (spkr_dock_prop <= 0) {
+	    LOGE("dock property conversion error");
+	    return -EINVAL;
+        }
+        close(fd);
+        LOGV("buffer = %s, spkr_dock_prop = 0x%lX", buffer, spkr_dock_prop);
+        spkr_dock_prop = spkr_dock_prop ^ basic_dock_prop;
+        LOGV("dock_prop returned = %lX", spkr_dock_prop);
+        return spkr_dock_prop;
+    } else {
+        if (already_warned == -1) {
+            LOGE("read_dock_prop failed to open %s\n", path);
+            already_warned = 1;
+        }
+        return -errno;
+    }
+}
+// ---------------------------------------------------------------------------------------------
+// Echo Canceller thread
+// Needed to isolate the EC/NS module from scheduling jitter of it's clients.
+//
+AudioPostProcessor::EcnsThread::EcnsThread() :
+    mReadBuf(0), mIsRunning(0)
+{
+}
+
+AudioPostProcessor::EcnsThread::~EcnsThread()
+{
+}
+
+int AudioPostProcessor::EcnsThread::readData(int fd, void * buffer, int bytes, int rate,
+                                             AudioPostProcessor * pp)
+{
+    LOGV("%s: read %d bytes at %d rate", __FUNCTION__, bytes, rate);
+    Mutex::Autolock lock(mEcnsReadLock);
+    mProcessor = pp;
+    mFd = fd;
+    mClientBuf = buffer;
+    mReadSize = bytes;
+    mRate = rate;
+    if (!mIsRunning) {
+        LOGD("Create (run) the ECNS thread");
+        run("AudioPostProcessor::EcnsThread", ANDROID_PRIORITY_HIGHEST);
+        mIsRunning = true;
+    }
+    if (mEcnsReadCond.waitRelative(mEcnsReadLock, seconds(1)) != NO_ERROR) {
+        LOGE("%s: ECNS thread is stalled.", __FUNCTION__);
+        mClientBuf = 0;
+        return -1;
+    }
+    return bytes;
+}
+
+bool AudioPostProcessor::EcnsThread::threadLoop()
+{
+#ifdef DEBUG_TIMING
+    int count = 0;
+    int small_jitter = 0;
+    int medium_jitter = 0;
+    int large_jitter = 0;
+#endif
+    ssize_t ret1 = 0, ret2;
+    struct timeval tv1, tv2;
+    int  usecs;
+    bool half_done = false;
+
+    LOGD("%s: Enter thread loop size %d rate %d", __FUNCTION__,
+                                          mReadSize, mRate);
+
+    mReadBuf = (int16_t *) malloc(mReadSize);
+    if (!mReadBuf)
+        goto error;
+
+    while (mProcessor->isEcnsEnabled()) {
+        GETTIMEOFDAY(&mtv1, NULL);
+        if (!half_done)
+            ret1 = ::read(mFd, mReadBuf, mReadSize/2);
+        GETTIMEOFDAY(&mtv2, NULL);
+        ret2 = ::read(mFd, (char *)mReadBuf+mReadSize/2, mReadSize/2);
+        if(!mProcessor->isEcnsEnabled())
+            goto error;
+        if (ret1 <= 0 || ret2 <= 0) {
+            LOGE("%s: Problem reading.", __FUNCTION__);
+            goto error;
+        }
+        GETTIMEOFDAY(&mtv3, NULL);
+        mEcnsReadLock.lock();
+        mProcessor->applyUplinkEcns(mReadBuf, mReadSize, mRate);
+        if (mClientBuf && mReadSize) {
+            // Give the buffer to the client.
+            memcpy(mClientBuf, mReadBuf, mReadSize);
+            // Avoid read overflow by reading before signaling the similar-priority read thread.
+            ret1 = ::read(mFd, mReadBuf, mReadSize/2);
+            half_done = true;
+            GETTIMEOFDAY(&mtv7, NULL);
+            mEcnsReadCond.signal();
+            mClientBuf = 0;
+        } else {
+            half_done = false;
+            LOGD("%s: Read overflow (ECNS sanity preserved)", __FUNCTION__);
+        }
+        mEcnsReadLock.unlock();
+        GETTIMEOFDAY(&mtv8, NULL);
+
+#ifdef DEBUG_TIMING
+	count++;
+        tv1.tv_sec = mtv1.tv_sec;
+        tv1.tv_usec = mtv1.tv_usec;
+        tv2.tv_sec = mtv8.tv_sec;
+        tv2.tv_usec = mtv8.tv_usec;
+        // Compare first and last timestamps
+        tv2.tv_sec -= tv1.tv_sec;
+        if(tv2.tv_usec < tv1.tv_usec) {
+            tv2.tv_sec--;
+            tv2.tv_usec = 1000000 + tv2.tv_usec - tv1.tv_usec;
+        } else {
+            tv2.tv_usec = tv2.tv_usec - tv1.tv_usec;
+        }
+        usecs = tv2.tv_usec + tv2.tv_sec*1000000;
+        if (usecs > 25000) {
+            if (usecs > 30000)
+                large_jitter++;
+            else
+                medium_jitter++;
+            LOGD("jitter: usecs = %d should be 20000", usecs);
+            LOGD("Point 1 (      start): %03d.%06d:", (int)mtv1.tv_sec, (int)mtv1.tv_usec);
+            LOGD("Point 2 (after read1): %03d.%06d:", (int)mtv2.tv_sec, (int)mtv2.tv_usec);
+            LOGD("Point 3 (after read2): %03d.%06d:", (int)mtv3.tv_sec, (int)mtv3.tv_usec);
+            LOGD("Point 4 (before ECNS): %03d.%06d:", (int)mtv4.tv_sec, (int)mtv4.tv_usec);
+            LOGD("Point 5 (after  ECNS): %03d.%06d:", (int)mtv5.tv_sec, (int)mtv5.tv_usec);
+            LOGD("Point 6 (after write): %03d.%06d:", (int)mtv6.tv_sec, (int)mtv6.tv_usec);
+            LOGD("Point 7 (before sgnl): %03d.%06d:", (int)mtv7.tv_sec, (int)mtv7.tv_usec);
+            LOGD("Point 8 (after unlck): %03d.%06d:", (int)mtv8.tv_sec, (int)mtv8.tv_usec);
+        } else if ((usecs > 22000) || (usecs < 18000)) {
+            small_jitter++;
+            LOGD("jitter: usecs = %d should be 20000", usecs);
+        }
+        if ((count % 500)== 0) {
+            LOGD("====================================== Statistics ===========================");
+            LOGD(" After %d seconds:", count/50);
+            LOGD(" Small jitters-  %d (%02.5f%%)", small_jitter, ((float)small_jitter)*100/count);
+            LOGD(" Medium jitters- %d (%02.5f%%)", medium_jitter, ((float)medium_jitter)*100/count);
+            LOGD(" Large jitters-  %d (%02.5f%%)", large_jitter, ((float)large_jitter)*100/count);
+            LOGD("=============================================================================");
+        }
+#endif
+    }
+error:
+    LOGD("%s: Exit thread loop, enabled = %d", __FUNCTION__,mProcessor->isEcnsEnabled());
+    if (mReadBuf) {
+        free (mReadBuf);
+        mReadBuf = 0;
+    }
+    mIsRunning = false;
+    return false;
+}
+
+} //namespace android
diff --git a/libaudio/AudioPostProcessor.h b/libaudio/AudioPostProcessor.h
new file mode 100644
index 0000000..0d8201d
--- /dev/null
+++ b/libaudio/AudioPostProcessor.h
@@ -0,0 +1,122 @@
+/*
+** Copyright 2010, 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 ANDROID_AUDIO_POST_PROCESSOR_H
+#define ANDROID_AUDIO_POST_PROCESSOR_H
+#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
+
+extern "C" {
+#include "cto_audio_mm.h"
+}
+#include "mot_acoustics.h"
+
+namespace android {
+
+class AudioPostProcessor
+{
+public:
+                        AudioPostProcessor();
+                        ~AudioPostProcessor();
+            void        setPlayAudioRate(int rate);
+            void        setAudioDev(struct cpcap_audio_stream *outDev,
+                                    struct cpcap_audio_stream *inDev,
+                                    bool is_bt, bool is_bt_ec, bool is_spdif);
+            void        doMmProcessing(void * buffer, int numSamples);
+            int         getEcnsRate(void);
+
+            void        enableEcns(bool value);
+            int         writeDownlinkEcns(int fd, void * buffer,
+                                          bool stereo, int bytes, Mutex * fdLockp);
+            int         read(int fd, void * buffer, int bytes, int rate);
+            int         applyUplinkEcns(void * buffer, int bytes, int rate);
+            bool        isEcnsEnabled(void) { return mEcnsEnabled; };
+
+private:
+            void        configMmAudio(void);
+            uint32_t    convOutDevToCTO(uint32_t outDev);
+            uint32_t    convRateToCto(uint32_t rate);
+
+            void        initEcns(int rate, int bytes);
+            void        stopEcns(void);
+            void        cleanupEcns(void);
+            void        ecnsLogToRam(int bytes);
+            void        ecnsLogToFile(void);
+            int         read_dock_prop(char const *path);
+
+        // CTO Multimedia Audio Processing storage buffers
+            int16_t     mPcmLoggingBuf[((CTO_AUDIO_MM_DATALOGGING_BUFFER_BLOCK_BYTESIZE)/2)];
+            uint32_t    mNoiseEst[((CTO_AUDIO_MM_NOISE_EST_BLOCK_BYTESIZE)/4)];
+            uint16_t    mRuntimeParam[((CTO_AUDIO_MM_RUNTIME_PARAM_BYTESIZE)/2)];
+            uint16_t    mStaticMem[((CTO_AUDIO_MM_STATICMEM_BLOCK_BYTESIZE)/2)];
+            uint16_t    mScratchMem[((CTO_AUDIO_MM_SCRATCHMEM_BLOCK_BYTESIZE)/2)];
+            CTO_AUDIO_MM_ENV_VAR mAudioMmEnvVar;
+            Mutex       mMmLock;
+
+        // EC/NS configuration etc.
+            Mutex       mEcnsBufLock;
+            Condition   mEcnsBufCond;  // Signal to unblock write thread
+            bool        mEcnsEnabled; // Enabled by libaudio
+            bool        mEcnsRunning; // ECNS module init done by read thread
+            int         mEcnsRate;
+            void *      mEcnsScratchBuf;  // holding cell for downlink speech "consumed".
+            int         mEcnsScratchBufSize;
+            void *      mEcnsOutBuf;      // buffer from downlink "write()"
+            int         mEcnsOutBufSize;
+            int         mEcnsOutBufReadOffset;
+            int         mEcnsOutFd;       // fd pointing to output driver
+            Mutex *     mEcnsOutFdLockp;
+            CTO_AUDIO_USECASES_CTRL mEcnsMode;
+            char *      mLogBuf[15];
+            int         mLogOffset;
+            int         mLogSize;
+            int         mLogNumPoints;
+            uint16_t    mLogPoint[15];
+            int16_t *   mEcnsDlBuf;
+            int         mEcnsDlBufSize;
+            bool        mEcnsOutStereo;
+
+        // EC/NS Module memory
+            T_MOT_MEM_BLOCKS mMemBlocks;
+            T_MOT_CTRL  mEcnsCtrl;
+            uint16_t    mStaticMemory_1[API_MOT_STATIC_MEM_WORD16_SIZE];
+            uint16_t    mMotDatalog[API_MOT_DATALOGGING_MEM_WORD16_SIZE];
+            uint16_t    mParamTable[AUDIO_PROFILE_PARAMETER_BLOCK_WORD16_SIZE*CTO_AUDIO_USECASE_TOTAL_NUMBER];
+
+        // ECNS Thread
+            class EcnsThread : public Thread {
+public:
+                        EcnsThread();
+                        ~EcnsThread();
+            int         readData(int fd, void * buffer, int bytes, int rate,
+                                 AudioPostProcessor * pp);
+private:
+            bool        threadLoop();
+            Mutex       mEcnsReadLock;
+            Condition   mEcnsReadCond;  // Signal to unblock read thread
+            AudioPostProcessor * mProcessor;
+            void *      mClientBuf;
+            int         mReadSize;
+            int16_t *   mReadBuf;
+            int         mFd;
+            int         mRate;
+            bool        mIsRunning;
+            };
+            sp <EcnsThread> mEcnsThread;
+};
+} // namespace android
+
+#endif // USE_PROPRIETARY_AUDIO_EXTENSIONS
+#endif // ANDROID_AUDIO_POST_PROCESSOR_H
diff --git a/libaudio/MODULE_LICENSE_APACHE2 b/libaudio/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libaudio/MODULE_LICENSE_APACHE2
diff --git a/libaudio/NOTICE b/libaudio/NOTICE
new file mode 100644
index 0000000..3237da6
--- /dev/null
+++ b/libaudio/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2008-2009, 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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/mXT1386_08_AA.bin b/mXT1386_08_AA.bin
new file mode 100755
index 0000000..bd91e84
--- /dev/null
+++ b/mXT1386_08_AA.bin
Binary files differ
diff --git a/mXT1386_08_E1.bin b/mXT1386_08_E1.bin
new file mode 100755
index 0000000..167276f
--- /dev/null
+++ b/mXT1386_08_E1.bin
Binary files differ
diff --git a/mXT1386_09_AA.bin b/mXT1386_09_AA.bin
new file mode 100644
index 0000000..12bac04
--- /dev/null
+++ b/mXT1386_09_AA.bin
Binary files differ
diff --git a/mXT1386_10_AA.bin b/mXT1386_10_AA.bin
new file mode 100644
index 0000000..b227045
--- /dev/null
+++ b/mXT1386_10_AA.bin
Binary files differ
diff --git a/mXT1386_10_FF.bin b/mXT1386_10_FF.bin
new file mode 100644
index 0000000..593cdc4
--- /dev/null
+++ b/mXT1386_10_FF.bin
Binary files differ
diff --git a/media_profiles.xml b/media_profiles.xml
new file mode 100644
index 0000000..2f24df3
--- /dev/null
+++ b/media_profiles.xml
@@ -0,0 +1,489 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+<!DOCTYPE MediaSettings [
+  <!ELEMENT MediaSettings (CamcorderProfiles,
+                         EncoderOutputFileFormat+,
+                         VideoEncoderCap+,
+                         AudioEncoderCap+,
+                         VideoDecoderCap,
+                         AudioDecoderCap)>
+  <!ELEMENT CamcorderProfiles (EncoderProfile+, ImageEncoding+, ImageDecoding, Camera)>
+  <!ELEMENT EncoderProfile (Video, Audio)>
+  <!ATTLIST EncoderProfile quality (timelapse1080p|timelapse720p|timelapse480p|timelapsehigh|timelapselow|480p|qcif|high|low) #REQUIRED>
+  <!ATTLIST EncoderProfile fileFormat (mp4|3gp) #REQUIRED>
+  <!ATTLIST EncoderProfile duration (30|60) #REQUIRED>
+  <!ELEMENT Video EMPTY>
+  <!ATTLIST Video codec (h264|h263|m4v) #REQUIRED>
+  <!ATTLIST Video bitRate CDATA #REQUIRED>
+  <!ATTLIST Video width CDATA #REQUIRED>
+  <!ATTLIST Video height CDATA #REQUIRED>
+  <!ATTLIST Video frameRate CDATA #REQUIRED>
+  <!ELEMENT Audio EMPTY>
+  <!ATTLIST Audio codec (amrnb|amrwb|aac) #REQUIRED>
+  <!ATTLIST Audio bitRate CDATA #REQUIRED>
+  <!ATTLIST Audio sampleRate CDATA #REQUIRED>
+  <!ATTLIST Audio channels (1|2) #REQUIRED>
+  <!ELEMENT ImageEncoding EMPTY>
+  <!ATTLIST ImageEncoding quality (90|80|70|60|50|40) #REQUIRED>
+  <!ELEMENT ImageDecoding EMPTY>
+  <!ATTLIST ImageDecoding memCap CDATA #REQUIRED>
+  <!ELEMENT Camera EMPTY>
+  <!ATTLIST Camera previewFrameRate CDATA #REQUIRED>
+  <!ELEMENT EncoderOutputFileFormat EMPTY>
+  <!ATTLIST EncoderOutputFileFormat name (mp4|3gp) #REQUIRED>
+  <!ELEMENT VideoEncoderCap EMPTY>
+  <!ATTLIST VideoEncoderCap name (h264|h263|m4v|wmv) #REQUIRED>
+  <!ATTLIST VideoEncoderCap enabled (true|false) #REQUIRED>
+  <!ATTLIST VideoEncoderCap minBitRate CDATA #REQUIRED>
+  <!ATTLIST VideoEncoderCap maxBitRate CDATA #REQUIRED>
+  <!ATTLIST VideoEncoderCap minFrameWidth CDATA #REQUIRED>
+  <!ATTLIST VideoEncoderCap maxFrameWidth CDATA #REQUIRED>
+  <!ATTLIST VideoEncoderCap minFrameHeight CDATA #REQUIRED>
+  <!ATTLIST VideoEncoderCap maxFrameHeight CDATA #REQUIRED>
+  <!ATTLIST VideoEncoderCap minFrameRate CDATA #REQUIRED>
+  <!ATTLIST VideoEncoderCap maxFrameRate CDATA #REQUIRED>
+  <!ELEMENT AudioEncoderCap EMPTY>
+  <!ATTLIST AudioEncoderCap name (amrnb|amrwb|aac|wma) #REQUIRED>
+  <!ATTLIST AudioEncoderCap enabled (true|false) #REQUIRED>
+  <!ATTLIST AudioEncoderCap minBitRate CDATA #REQUIRED>
+  <!ATTLIST AudioEncoderCap maxBitRate CDATA #REQUIRED>
+  <!ATTLIST AudioEncoderCap minSampleRate CDATA #REQUIRED>
+  <!ATTLIST AudioEncoderCap maxSampleRate CDATA #REQUIRED>
+  <!ATTLIST AudioEncoderCap minChannels (1|2) #REQUIRED>
+  <!ATTLIST AudioEncoderCap maxChannels (1|2) #REQUIRED>
+  <!ELEMENT VideoDecoderCap EMPTY>
+  <!ATTLIST VideoDecoderCap name (wmv) #REQUIRED>
+  <!ATTLIST VideoDecoderCap enabled (true|false) #REQUIRED>
+  <!ELEMENT AudioDecoderCap EMPTY>
+  <!ATTLIST AudioDecoderCap name (wma) #REQUIRED>
+  <!ATTLIST AudioDecoderCap enabled (true|false) #REQUIRED>
+]>
+<!--
+     This file is used to declare the multimedia profiles and capabilities
+     on an android-powered device.
+-->
+<MediaSettings>
+  <!-- Each camcorder profile defines a set of predefined configuration parameters -->
+  <!-- Back Camera -->
+  <CamcorderProfiles cameraId="0">
+
+    <EncoderProfile quality="low" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="384000"
+             width="176"
+             height="144"
+             frameRate="30" />
+
+      <Audio codec="amrnb"
+             bitRate="12200"
+             sampleRate="8000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="high" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="8000000"
+             width="1280"
+             height="720"
+             frameRate="30" />
+
+      <Audio codec="aac"
+             bitRate="96000"
+             sampleRate="16000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="qcif" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="384000"
+             width="176"
+             height="144"
+             frameRate="30" />
+
+      <Audio codec="amrnb"
+             bitRate="12200"
+             sampleRate="8000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="cif" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="1536000"
+             width="352"
+             height="288"
+             frameRate="30" />
+
+      <Audio codec="amrnb"
+             bitRate="12200"
+             sampleRate="8000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="480p" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="4000000"
+             width="640"
+             height="480"
+             frameRate="30" />
+
+      <Audio codec="aac"
+             bitRate="96000"
+             sampleRate="16000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="720p" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="8000000"
+             width="1280"
+             height="720"
+             frameRate="30" />
+
+      <Audio codec="aac"
+             bitRate="96000"
+             sampleRate="16000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="timelapselow" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="384000"
+             width="176"
+             height="144"
+             frameRate="30" />
+
+      <!--
+            The Audio part of the profile will not be used since time lapse mode
+            does not capture audio
+      -->
+      <Audio codec="amrnb"
+             bitRate="12200"
+             sampleRate="8000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="timelapsehigh" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="14000000"
+             width="1920"
+             height="1088"
+             frameRate="30" />
+
+      <!--
+            The Audio part of the profile will not be used since time lapse mode
+            does not capture audio
+      -->
+      <Audio codec="aac"
+             bitRate="96000"
+             sampleRate="16000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="timelapseqcif" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="384000"
+             width="176"
+             height="144"
+             frameRate="30" />
+
+      <!--
+            The Audio part of the profile will not be used since time lapse mode
+            does not capture audio
+      -->
+      <Audio codec="amrnb"
+             bitRate="12200"
+             sampleRate="8000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="timelapsecif" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="1536000"
+             width="352"
+             height="288"
+             frameRate="30" />
+
+      <!--
+            The Audio part of the profile will not be used since time lapse mode
+            does not capture audio
+      -->
+      <Audio codec="amrnb"
+             bitRate="12200"
+             sampleRate="8000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="timelapse480p" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="4000000"
+             width="640"
+             height="480"
+             frameRate="30" />
+
+      <!--
+            The Audio part of the profile will not be used since time lapse mode
+            does not capture audio
+      -->
+      <Audio codec="aac"
+             bitRate="96000"
+             sampleRate="16000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="timelapse720p" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="8000000"
+             width="1280"
+             height="720"
+             frameRate="30" />
+
+      <!--
+            The Audio part of the profile will not be used since time lapse mode
+            does not capture audio
+      -->
+      <Audio codec="aac"
+             bitRate="96000"
+             sampleRate="16000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="timelapse1080p" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="14000000"
+             width="1920"
+             height="1088"
+             frameRate="30" />
+
+      <!--
+            The Audio part of the profile will not be used since time lapse mode
+            does not capture audio
+      -->
+      <Audio codec="aac"
+             bitRate="96000"
+             sampleRate="16000"
+             channels="1" />
+    </EncoderProfile>
+
+    <ImageEncoding quality="90" />
+    <ImageEncoding quality="80" />
+    <ImageEncoding quality="70" />
+    <ImageDecoding memCap="20000000" />
+
+  </CamcorderProfiles>
+
+  <!-- Front Camera -->
+  <CamcorderProfiles cameraId="1">
+
+    <EncoderProfile quality="low" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="384000"
+             width="176"
+             height="144"
+             frameRate="30" />
+
+      <Audio codec="amrnb"
+             bitRate="12200"
+             sampleRate="8000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="high" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="4000000"
+             width="640"
+             height="480"
+             frameRate="30" />
+
+      <Audio codec="aac"
+             bitRate="96000"
+             sampleRate="16000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="qcif" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="384000"
+             width="176"
+             height="144"
+             frameRate="30" />
+
+      <Audio codec="amrnb"
+             bitRate="12200"
+             sampleRate="8000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="cif" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="1536000"
+             width="352"
+             height="288"
+             frameRate="30" />
+
+      <Audio codec="amrnb"
+             bitRate="12200"
+             sampleRate="8000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="480p" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="4000000"
+             width="640"
+             height="480"
+             frameRate="30" />
+
+      <Audio codec="aac"
+             bitRate="96000"
+             sampleRate="16000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="timelapselow" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="384000"
+             width="176"
+             height="144"
+             frameRate="30" />
+
+      <!--
+            The Audio part of the profile will not be used since time lapse mode
+            does not capture audio
+      -->
+      <Audio codec="amrnb"
+             bitRate="12200"
+             sampleRate="8000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="timelapsehigh" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="4000000"
+             width="640"
+             height="480"
+             frameRate="30" />
+
+      <!--
+            The Audio part of the profile will not be used since time lapse mode
+            does not capture audio
+      -->
+      <Audio codec="aac"
+             bitRate="96000"
+             sampleRate="16000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="timelapseqcif" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="384000"
+             width="176"
+             height="144"
+             frameRate="30" />
+
+      <!--
+            The Audio part of the profile will not be used since time lapse mode
+            does not capture audio
+      -->
+      <Audio codec="amrnb"
+             bitRate="12200"
+             sampleRate="8000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="timelapsecif" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="1536000"
+             width="352"
+             height="288"
+             frameRate="30" />
+
+      <!--
+            The Audio part of the profile will not be used since time lapse mode
+            does not capture audio
+      -->
+      <Audio codec="amrnb"
+             bitRate="12200"
+             sampleRate="8000"
+             channels="1" />
+    </EncoderProfile>
+
+    <EncoderProfile quality="timelapse480p" fileFormat="3gp" duration="30">
+      <Video codec="h264"
+             bitRate="4000000"
+             width="640"
+             height="480"
+             frameRate="30" />
+
+      <!--
+            The Audio part of the profile will not be used since time lapse mode
+            does not capture audio
+      -->
+      <Audio codec="aac"
+             bitRate="96000"
+             sampleRate="16000"
+             channels="1" />
+    </EncoderProfile>
+
+    <ImageEncoding quality="90" />
+    <ImageEncoding quality="80" />
+    <ImageEncoding quality="70" />
+    <ImageDecoding memCap="20000000" />
+
+  </CamcorderProfiles>
+
+  <EncoderOutputFileFormat name="3gp" />
+
+  <!--
+         If a codec is not enabled, it is invisible to the applications
+         In other words, the applications won't be able to use the codec
+         or query the capabilities of the codec at all if it is disabled
+    -->
+  <VideoEncoderCap name="h264" enabled="true"
+      minBitRate="64000" maxBitRate="14000000"
+      minFrameWidth="176" maxFrameWidth="1920"
+      minFrameHeight="144" maxFrameHeight="1088"
+      minFrameRate="1" maxFrameRate="30" />
+
+  <VideoEncoderCap name="h263" enabled="true"
+      minBitRate="64000" maxBitRate="8000000"
+      minFrameWidth="176" maxFrameWidth="704"
+      minFrameHeight="144" maxFrameHeight="576"
+      minFrameRate="1" maxFrameRate="30" />
+
+  <VideoEncoderCap name="m4v" enabled="true"
+      minBitRate="64000" maxBitRate="10000000"
+      minFrameWidth="176" maxFrameWidth="1280"
+      minFrameHeight="144" maxFrameHeight="720"
+      minFrameRate="1" maxFrameRate="30" />
+
+  <AudioEncoderCap name="aac" enabled="true"
+      minBitRate="8000" maxBitRate="320000"
+      minSampleRate="8000" maxSampleRate="48000"
+      minChannels="1" maxChannels="1" />
+
+  <AudioEncoderCap name="amrwb" enabled="true"
+        minBitRate="6600" maxBitRate="23050"
+        minSampleRate="16000" maxSampleRate="16000"
+        minChannels="1" maxChannels="1" />
+
+  <AudioEncoderCap name="amrnb" enabled="true"
+      minBitRate="4750" maxBitRate="12200"
+      minSampleRate="8000" maxSampleRate="8000"
+      minChannels="1" maxChannels="1" />
+
+  <VideoDecoderCap name="wmv" enabled="false"/>
+  <AudioDecoderCap name="wma" enabled="false"/>
+</MediaSettings>
diff --git a/qtouch-touchscreen.idc b/qtouch-touchscreen.idc
new file mode 100644
index 0000000..28224dd
--- /dev/null
+++ b/qtouch-touchscreen.idc
@@ -0,0 +1,55 @@
+# Copyright (C) 2010 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.
+
+#
+# Input Device Configuration File for the Stingray touch screen.
+#
+# These calibration values are derived from empirical measurements
+# and may not be appropriate for use with other touch screens.
+# Refer to the input device configuration documentation for more details.
+#
+
+# Basic Parameters
+touch.deviceType = touchScreen
+touch.orientationAware = 1
+
+# Touch Size
+touch.touchSize.calibration = pressure
+
+# Tool Size
+# Driver reports tool size as an area measurement.
+#
+# Based on empirical measurements, we estimate the size of the tool
+# using size = sqrt(22 * rawToolArea + 0) * 6 + 0.
+touch.toolSize.calibration = area
+touch.toolSize.areaScale = 22
+touch.toolSize.areaBias = 0
+touch.toolSize.linearScale = 6
+touch.toolSize.linearBias = 0
+touch.toolSize.isSummed = 0
+
+# Pressure
+# Driver reports signal strength as pressure.
+#
+# A normal index finger touch typically registers about 80 signal strength
+# units although we don't expect these values to be accurate.
+touch.pressure.calibration = amplitude
+touch.pressure.source = default
+touch.pressure.scale = 0.0125
+
+# Size
+touch.size.calibration = normalized
+
+# Orientation
+touch.orientation.calibration = vector
diff --git a/recovery.fstab b/recovery.fstab
new file mode 100644
index 0000000..fd96e7a
--- /dev/null
+++ b/recovery.fstab
@@ -0,0 +1,10 @@
+# mount point	fstype		device
+
+/sdcard		vfat		/dev/block/sda1
+/system		ext4		/dev/block/platform/sdhci-tegra.3/by-name/system
+/cache		ext4		/dev/block/platform/sdhci-tegra.3/by-name/cache
+/data		ext4		/dev/block/platform/sdhci-tegra.3/by-name/userdata
+/misc 		emmc 		/dev/block/platform/sdhci-tegra.3/by-name/misc
+/boot		emmc		/dev/block/platform/sdhci-tegra.3/by-name/boot
+/recovery	emmc		/dev/block/platform/sdhci-tegra.3/by-name/recovery
+
diff --git a/ril/Android.mk b/ril/Android.mk
new file mode 100644
index 0000000..fecf96d
--- /dev/null
+++ b/ril/Android.mk
@@ -0,0 +1,49 @@
+LOCAL_PATH := $(call my-dir)
+
+ifneq ($(AP_MODEM_CDMA_BLDSRC),1)
+file := $(TARGET_OUT_SHARED_LIBRARIES)/libmoto_ril.so
+$(file) : $(LOCAL_PATH)/libmoto_ril.so | $(ACP)
+	$(transform-prebuilt-to-target)
+ALL_PREBUILT += $(file)
+endif
+
+ifneq ($(AP_MODEM_CDMA_BLDSRC),1)
+file := $(PRODUCT_OUT)/obj/lib/libmoto_ril.so
+$(file) : $(LOCAL_PATH)/libmoto_ril.so | $(ACP)
+	$(transform-prebuilt-to-target)
+ALL_PREBUILT += $(file)
+endif
+
+ifneq ($(AP_MODEM_CDMA_BLDSRC),1)
+file := $(TARGET_OUT_SHARED_LIBRARIES)/libpppd_plugin-ril.so
+$(file) : $(LOCAL_PATH)/libpppd_plugin-ril.so | $(ACP)
+	$(transform-prebuilt-to-target)
+ALL_PREBUILT += $(file)
+endif
+
+ifneq ($(AP_MODEM_CDMA_BLDSRC),1)
+file := $(TARGET_OUT_SHARED_LIBRARIES)/libril_rds.so
+$(file) : $(LOCAL_PATH)/libril_rds.so | $(ACP)
+	$(transform-prebuilt-to-target)
+ALL_PREBUILT += $(file)
+endif
+
+ifneq ($(AP_MODEM_CDMA_BLDSRC),1)
+file := $(TARGET_OUT_EXECUTABLES)/chat-ril
+$(file) : $(LOCAL_PATH)/chat-ril | $(ACP)
+	$(transform-prebuilt-to-target)
+ALL_PREBUILT += $(file)
+endif
+
+ifneq ($(AP_MODEM_CDMA_BLDSRC),1)
+file := $(TARGET_OUT_EXECUTABLES)/pppd-ril
+$(file) : $(LOCAL_PATH)/pppd-ril | $(ACP)
+	$(transform-prebuilt-to-target)
+ALL_PREBUILT += $(file)
+endif
+
+file := $(TARGET_OUT_ETC)/ppp/peers/pppd-ril.options
+$(file) : $(LOCAL_PATH)/pppd-ril.options | $(ACP)
+	$(transform-prebuilt-to-target)
+ALL_PREBUILT += $(file)
+
diff --git a/ril/chat-ril b/ril/chat-ril
new file mode 100755
index 0000000..0f48e24
--- /dev/null
+++ b/ril/chat-ril
Binary files differ
diff --git a/ril/libmoto_ril.so b/ril/libmoto_ril.so
new file mode 100644
index 0000000..1252f06
--- /dev/null
+++ b/ril/libmoto_ril.so
Binary files differ
diff --git a/ril/libpppd_plugin-ril.so b/ril/libpppd_plugin-ril.so
new file mode 100644
index 0000000..e1ee999
--- /dev/null
+++ b/ril/libpppd_plugin-ril.so
Binary files differ
diff --git a/ril/libril_rds.so b/ril/libril_rds.so
new file mode 100644
index 0000000..064ae07
--- /dev/null
+++ b/ril/libril_rds.so
Binary files differ
diff --git a/ril/pppd-ril b/ril/pppd-ril
new file mode 100755
index 0000000..509c777
--- /dev/null
+++ b/ril/pppd-ril
Binary files differ
diff --git a/ril/pppd-ril.options b/ril/pppd-ril.options
new file mode 100644
index 0000000..89ecac6
--- /dev/null
+++ b/ril/pppd-ril.options
@@ -0,0 +1,19 @@
+nodetach
+debug
+noauth
+defaultroute
+usepeerdns
+connect-delay 1000
+# Don't remove the user/password lines.  They are required to make PPPD authenticate itself with
+# the BP when doing Simple IP (SIP).  The BP will replace the user/password strings with the correct
+# values when authenticating to the network-side PPP peer.
+user NotUsed@nobody.com
+password NotUsed
+crtscts
+lcp-echo-failure 0
+lcp-echo-interval 0
+ipcp-max-configure 30
+ipcp-max-failure 30
+ipcp-max-terminate 10
+novj
+linkname ril
diff --git a/ril/tty2ttyd b/ril/tty2ttyd
new file mode 100755
index 0000000..c2331cf
--- /dev/null
+++ b/ril/tty2ttyd
Binary files differ
diff --git a/stingray-keypad.kcm b/stingray-keypad.kcm
new file mode 100644
index 0000000..7ee6e5a
--- /dev/null
+++ b/stingray-keypad.kcm
@@ -0,0 +1,15 @@
+# Copyright (C) 2010 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.
+
+type SPECIAL_FUNCTION
diff --git a/stingray-keypad.kl b/stingray-keypad.kl
new file mode 100644
index 0000000..c9735da
--- /dev/null
+++ b/stingray-keypad.kl
@@ -0,0 +1,16 @@
+# Copyright (C) 2010 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.
+
+key 114   VOLUME_DOWN     WAKE
+key 115   VOLUME_UP       WAKE
diff --git a/system.prop b/system.prop
new file mode 100644
index 0000000..641a60d
--- /dev/null
+++ b/system.prop
@@ -0,0 +1,22 @@
+# RIL and telephony related settings
+rild.libargs=-d /dev/chnlat10
+rild.libpath=/system/lib/libmoto_ril.so
+persist.ril.mux.ttydevice=/dev/usb/tty2-1:1.2
+persist.ril.mux.noofchannels=8
+persist.ril.modem.mode=1
+ro.cdma.home.operator.numeric=310004
+ro.cdma.home.operator.alpha=Verizon
+ro.cdma.homesystem=64,65,76,77,78,79,80,81,82,83
+ro.cdma.data_retry_config=default_randomization=2000,0,0,120000,180000,540000,960000
+persist.ril.modem.ttydevice=/dev/usb/tty2-1:1.4
+ro.telephony.default_network=4
+
+# The OpenGL ES API level that is natively supported by this device.
+# This is a 16.16 fixed point number
+ro.opengles.version = 131072
+
+# Indicate carrier OTA SP number schema
+# refer to frameworks/base/telephony/java/com/android/
+# internal/telephony/cdma/CDMAPhone.java for the schema:
+ro.cdma.otaspnumschema=SELC,1,80,99
+
diff --git a/taudio/Android.mk b/taudio/Android.mk
new file mode 100644
index 0000000..04c25d3
--- /dev/null
+++ b/taudio/Android.mk
@@ -0,0 +1,33 @@
+# Copyright 2006 The Android Open Source Project
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= tctl.c
+LOCAL_MODULE_TAGS:= eng
+LOCAL_MODULE:= tctl
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= tplay.c
+LOCAL_MODULE_TAGS:= eng
+LOCAL_MODULE:= tplay
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= trec.c
+LOCAL_MODULE_TAGS:= eng
+LOCAL_MODULE:= trec
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= twav.c
+LOCAL_MODULE_TAGS:= eng
+LOCAL_MODULE:= twav
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= resample.c
+LOCAL_MODULE_TAGS:= eng
+LOCAL_MODULE:= tdownsample
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/taudio/resample.c b/taudio/resample.c
new file mode 100644
index 0000000..39f2c39
--- /dev/null
+++ b/taudio/resample.c
@@ -0,0 +1,254 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+struct wav_header {
+    char  riff[4];
+    uint32_t chunk_size;
+    char  format[4];
+
+    char  subchunk1_id[4];
+    uint32_t subchunk1_size;
+    uint16_t audio_format;
+    uint16_t num_channels;
+    uint32_t sample_rate;
+    uint32_t byte_rate;
+    uint16_t block_align;
+    uint16_t bits_per_sample;
+
+    char  subchunk2_id[4];
+    uint32_t subchunk2_size;
+} __attribute__((packed));
+
+static void init_wav_header(struct wav_header *hdr,
+                       uint32_t num_samples,
+                       uint16_t bits_per_sample,
+                       int   channels,
+                       uint32_t sample_rate)
+{
+    hdr->riff[0] = 'R';
+    hdr->riff[1] = 'I';
+    hdr->riff[2] = 'F';
+    hdr->riff[3] = 'F';
+
+    hdr->subchunk2_size = num_samples * channels * bits_per_sample / 8;
+
+    hdr->chunk_size = 36 + hdr->subchunk2_size;
+    hdr->format[0] = 'W';
+    hdr->format[1] = 'A';
+    hdr->format[2] = 'V';
+    hdr->format[3] = 'E';
+
+    hdr->subchunk1_id[0] = 'f';
+    hdr->subchunk1_id[1] = 'm';
+    hdr->subchunk1_id[2] = 't';
+    hdr->subchunk1_id[3] = ' ';
+
+    hdr->subchunk1_size = 16;
+    hdr->audio_format = 1; /* PCM */
+    hdr->num_channels = channels;
+    hdr->sample_rate = sample_rate;
+    hdr->byte_rate = sample_rate * channels * bits_per_sample / 8;
+    hdr->block_align = channels * bits_per_sample / 8;
+    hdr->bits_per_sample = bits_per_sample;
+
+    hdr->subchunk2_id[0] = 'd';
+    hdr->subchunk2_id[1] = 'a';
+    hdr->subchunk2_id[2] = 't';
+    hdr->subchunk2_id[3] = 'a';
+}
+
+static const int divs_8000[] = { 5, 6, 6, 5 };
+static const int divs_11025[] = { 4 };
+static const int divs_22050[] = { 2 };
+static const int divs_44100[] = { 1 };
+
+int downsample(const int16_t *in, int16_t *out, int len, int *consumed,
+               const int *divs, int divs_len,
+               int out_stereo)
+{
+    int i, j, lsum, rsum;
+    int di, div;
+    int oi;
+
+    i = 0;
+    oi = 0;
+    di = 0;
+    div = divs[0];
+    while (i + div * 2 <= len) {
+//        printf("div %d, i %d, oi %d\n", div, i, oi);
+        for (j = 0, lsum = 0, rsum = 0; j < div; j++) {
+            lsum += in[i + j * 2];
+            rsum += in[i + j * 2 + 1];
+        }
+        if (!out_stereo)
+            out[oi] = (lsum + rsum) / (div * 2);
+        else {
+            out[oi] = lsum / div;
+            out[oi + 1] = rsum / div;
+        }
+
+        oi += out_stereo + 1;
+        i += div * 2;
+        div = divs[++di % divs_len];
+    }
+
+//    printf("done: i %d, len %d, oi %d\n", i, len, oi);
+    *consumed = i;
+    return oi;
+}
+
+#define FAILIF(x, ...) do if (x) { \
+    fprintf(stderr, __VA_ARGS__);  \
+    exit(EXIT_FAILURE);            \
+} while (0)
+
+int main(int argc, char **argv)
+{
+    int opt, ifd, ofd;
+    int new_rate = -1;
+    const int *divs;
+    int divs_len;
+    int consumed;
+    int channels = -1;
+    char *input = NULL;
+    char *output = NULL;
+    int nr, nr_out, nw;
+    int put_header = 0;
+    int total = 0;
+    const int bits_per_sample = 16;
+
+    struct wav_header src_hdr, dst_hdr;
+
+    int16_t buf[2048];
+
+    while ((opt = getopt(argc, argv, "o:s:c:w")) != -1) {
+        switch (opt) {
+        case 'o':
+            output = strdup(optarg);
+            break;
+        case 's':
+            new_rate = atoi(optarg);
+            break;
+        case 'c':
+            channels = atoi(optarg);
+            break;
+        case 'w':
+            put_header = 1;
+            break;
+        default: /* '?' */
+            fprintf(stderr, "usage: %s -o<outfile> -s<sampling> -c<channels>\n",
+                *argv);
+            fprintf(stderr, "usage: %s -o<outfile> -s<sampling> -c<channels>\n",
+                *argv);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    FAILIF(channels != 1 && channels != 2, "-c value must be 1 or 2\n");
+
+    switch(new_rate) {
+    case 8000:
+        divs = divs_8000;
+        divs_len = 4;
+        break;
+    case 11025:
+        divs = divs_11025;
+        divs_len = 1;
+        break;
+    case 22050:
+        divs = divs_22050;
+        divs_len = 1;
+        break;
+    case 44100:
+        divs = divs_44100;
+        divs_len = 1;
+        break;
+    default:
+        FAILIF(1, "rate %d is not supported\n", new_rate);
+    }
+
+    FAILIF(!output, "Expecting an output file name\n");
+    FAILIF(optind >= argc, "Expecting an input file name\n");
+
+    input = argv[optind];
+
+    printf("input file [%s]\n", input);
+    printf("output file [%s]\n", output);
+    printf("new rate: [%d]\n", new_rate);
+
+    ifd = open(input, O_RDONLY);
+    FAILIF(ifd < 0, "Could not open %s: %s\n", input, strerror(errno));
+    ofd = open(output, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+    FAILIF(ofd < 0, "Could not open %s: %s\n", output, strerror(errno));
+
+    if (strstr(input, ".wav")) {
+        FAILIF(read(ifd, &src_hdr, sizeof(src_hdr)) != sizeof(src_hdr),
+            "Failed to read input WAV header: %s\n",
+            strerror(errno));
+        FAILIF(src_hdr.audio_format != 1,
+            "Expecting PCM encoding\n");
+        FAILIF(src_hdr.sample_rate != 44100,
+            "Expecting 44.kHz files\n");
+        FAILIF(src_hdr.num_channels != 2,
+            "Expecting 2-channel files\n");
+        FAILIF(src_hdr.bits_per_sample != bits_per_sample,
+            "Expecting 16-bit PCM files\n");
+    }
+
+    if (put_header)
+        FAILIF(lseek(ofd, sizeof(struct wav_header), SEEK_SET) < 0,
+            "seek error in %s: %s\n", output, strerror(errno));
+
+    consumed = 0;
+    while (1) {
+        nr = read(ifd, buf + consumed, sizeof(buf) - consumed);
+        FAILIF(nr < 0, "could not read from %s: %s\n", input, strerror(errno));
+        if (!nr) {
+            printf("done\n");
+            break;
+        }
+        nr += consumed;
+
+//      printf("resampling %d samples\n", nr / 2);
+        nr_out = downsample(buf, buf, nr / 2, &consumed, divs, divs_len, channels == 2);
+        consumed *= 2;
+//      printf("done: %d samples were generated (consumed %d out of %d bytes)\n", nr_out, consumed, nr);
+
+        if (consumed < nr) {
+            memcpy(buf, buf + consumed, nr - consumed);
+            consumed = nr - consumed;
+//          printf("copied %d bytes to front\n", consumed);
+        }
+        else consumed = 0;
+
+        nr_out *= 2;
+        nw = write(ofd, buf, nr_out);
+        FAILIF(nw < 0, "could not write to %s: %s\n", output, strerror(errno));
+        FAILIF(nw != nr_out, "mismatch, generated %d, wrote %d bytes\n", nr_out, nw);
+        total += nw;
+    }
+
+    if (put_header) {
+        printf("writing WAV header\n");
+        lseek(ofd, 0, SEEK_SET);
+        init_wav_header(&dst_hdr,
+            total * 8 / (channels * bits_per_sample),
+            bits_per_sample,
+            channels,
+            new_rate);
+        FAILIF(write(ofd, &dst_hdr, sizeof(dst_hdr)) != sizeof(dst_hdr),
+            "Could not write WAV header: %s\n", strerror(errno));
+    }
+
+    close(ifd);
+    close(ofd);
+
+    return 0;
+}
diff --git a/taudio/tctl.c b/taudio/tctl.c
new file mode 100644
index 0000000..6bdd7bd
--- /dev/null
+++ b/taudio/tctl.c
@@ -0,0 +1,204 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <sys/ioctl.h>
+#include <linux/cpcap_audio.h>
+#include <linux/tegra_audio.h>
+
+#define FAILIF(x, ...) do if (x) { \
+    fprintf(stderr, __VA_ARGS__);  \
+    exit(EXIT_FAILURE);            \
+} while (0)
+
+static char buffer[4096];
+
+int
+main(int argc, char *argv[])
+{
+    int opt, cfd;
+    int output = -4;
+    int input = -3;
+    int volume = -1; /* max 15 */
+    int in_volume = -1; /* max 31 */
+    int record = -1; /* start 1, stop 0 */
+    int use_dma = -1;
+    int in_rate = -1;
+    int in_channels = -1;
+
+    while ((opt = getopt(argc, argv, "o::i::s:c:v::g::d:r:")) != -1) {
+        switch (opt) {
+        case 'o':
+            if (optarg)
+                output = atoi(optarg);
+            else
+                output = -5;
+            break;
+        case 'i':
+            if (optarg)
+                input = atoi(optarg);
+            else
+                input = -4;
+            break;
+        case 'v':
+            if (optarg)
+                volume = atoi(optarg);
+            else
+                volume = -2;
+            break;
+        case 's':
+            in_rate = atoi(optarg);
+            break;
+        case 'c':
+            in_channels = atoi(optarg);
+            break;
+        case 'g':
+            if (optarg)
+                in_volume = atoi(optarg);
+            else
+                in_volume = -2;
+            break;
+        case 'd':
+            use_dma = atoi(optarg);
+            break;
+        case 'r':
+            record = atoi(optarg);
+            break;
+        default: /* '?' */
+            fprintf(stderr, "Unknown option\n");
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    cfd = open("/dev/audio_ctl", O_RDWR);
+
+    printf("cfd opened\n");
+
+    FAILIF(cfd < 0, "could not open control: %s\n", strerror(errno));
+
+    if (output > -4 && output < 4) {
+        struct cpcap_audio_stream cfg;
+        assert(!output); // 1 or 2 or 3 or -1 or -2 or -3
+        if (output < 0) {
+            cfg.id = (-output) - 1;
+            cfg.on = 0;
+            printf("set output %d to OFF\n", cfg.id);
+        }
+        else {
+            cfg.id = output - 1;
+            cfg.on = 1;
+            printf("set output %d to ON\n", cfg.id);
+        }
+        FAILIF(ioctl(cfd, CPCAP_AUDIO_OUT_SET_OUTPUT, &cfg) < 0,
+               "Cannot set output device %d: %s\n", cfg.id, strerror(errno));
+    }
+    else if (output == -5) {
+        struct cpcap_audio_stream cfg;
+        FAILIF(ioctl(cfd, CPCAP_AUDIO_OUT_GET_OUTPUT, &cfg) < 0,
+               "Cannot get output device %d: %s\n", cfg.id, strerror(errno));
+        printf("current output: %d, %s\n", cfg.id, (cfg.on ? "on" : "off"));
+    }
+
+    if (volume >= 0) {
+        printf("set output volume\n");
+        FAILIF(ioctl(cfd, CPCAP_AUDIO_OUT_SET_VOLUME, volume) < 0,
+               "Cannot set volume to %d: %s\n", output, strerror(errno));
+    }
+    else if (volume == -2) {
+        FAILIF(ioctl(cfd, CPCAP_AUDIO_OUT_GET_VOLUME, &volume) < 0,
+               "Cannot get volume: %s\n", strerror(errno));
+        printf("speaker volume: %d\n", volume);
+    }
+
+    if (in_volume >= 0) {
+        printf("set input volume\n");
+        FAILIF(ioctl(cfd, CPCAP_AUDIO_IN_SET_VOLUME, in_volume) < 0,
+               "Cannot set input volume to %d: %s\n", output, strerror(errno));
+    }
+    else if (in_volume == -2) {
+        FAILIF(ioctl(cfd, CPCAP_AUDIO_IN_GET_VOLUME, &in_volume) < 0,
+               "Cannot get input volume: %s\n", strerror(errno));
+        printf("microphone gain: %d\n", in_volume);
+    }
+
+    if (input > -3 && input < 3) {
+        struct cpcap_audio_stream cfg;
+        assert(!input); // 1 or 2 or -1 or -2
+        if (input < 0) {
+            cfg.id = (-input) - 1;
+            cfg.on = 0;
+            printf("set input %d to OFF\n", cfg.id);
+        }
+        else {
+            cfg.id = input - 1;
+            cfg.on = 1;
+            printf("set input %d to ON\n", cfg.id);
+        }
+        FAILIF(ioctl(cfd, CPCAP_AUDIO_IN_SET_INPUT, &cfg) < 0,
+               "Cannot set input device %d: %s\n", cfg.id, strerror(errno));
+    }
+    else if (input == -4) {
+        struct cpcap_audio_stream cfg;
+        FAILIF(ioctl(cfd, CPCAP_AUDIO_IN_GET_INPUT, &cfg) < 0,
+               "Cannot get input device %d: %s\n", cfg.id, strerror(errno));
+        printf("current input: %d, %s\n", cfg.id, (cfg.on ? "on" : "off"));
+    }
+
+    if (in_channels >= 0 || in_rate >= 0) {
+        int recfd;
+        struct tegra_audio_in_config cfg;
+
+        printf("set input config\n");
+
+        printf("opening audio input\n");
+        recfd = open("/dev/audio1_in_ctl", O_RDWR);
+        FAILIF(recfd < 0, "could not open for recording: %s\n", strerror(errno));
+
+        printf("getting audio-input config\n");
+        FAILIF(ioctl(recfd, TEGRA_AUDIO_IN_GET_CONFIG, &cfg) < 0,
+               "could not get input config: %s\n", strerror(errno));
+        if (in_channels >= 0)
+            cfg.stereo = in_channels == 2;
+        if (in_rate >= 0)
+            cfg.rate = in_rate;
+        printf("setting audio-input config (stereo %d, rate %d)\n", cfg.stereo, cfg.rate);
+        FAILIF(ioctl(recfd, TEGRA_AUDIO_IN_SET_CONFIG, &cfg) < 0,
+               "could not set input config: %s\n", strerror(errno));
+        close(recfd);
+    }
+
+    if (use_dma >= 0) {
+        int piofd = open("/sys/kernel/debug/tegra_audio/dma", O_RDWR);
+        FAILIF(piofd < 0, "Could not open DMA/PIO toggle file: %s\n", strerror(errno));
+        if (use_dma)
+            FAILIF(write(piofd, "dma\n", sizeof("dma\n")) < 0,
+                   "Could not set to DMA: %s\n", strerror(errno));
+        else
+            FAILIF(write(piofd, "dma\n", sizeof("pio\n")) < 0,
+                   "Could not set to PIO: %s\n", strerror(errno));
+    }
+
+    if (record >= 0) {
+        printf("opening audio input\n");
+        int recfd = open("/dev/audio1_in_ctl", O_RDWR);
+        printf("done opening audio input\n");
+        FAILIF(recfd < 0, "could not open for recording: %s\n", strerror(errno));
+        if (record) {
+            printf("start recording\n");
+            FAILIF(ioctl(recfd, TEGRA_AUDIO_IN_START) < 0,
+                   "Could not start recording: %s\n", strerror(errno));
+        } else {
+            printf("stop recording\n");
+            FAILIF(ioctl(recfd, TEGRA_AUDIO_IN_STOP) < 0,
+                   "Could not stop recording: %s\n", strerror(errno));
+        }
+    }
+
+    return 0;
+}
+
diff --git a/taudio/tplay.c b/taudio/tplay.c
new file mode 100644
index 0000000..65152e7
--- /dev/null
+++ b/taudio/tplay.c
@@ -0,0 +1,102 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <sys/ioctl.h>
+#include <linux/tegra_audio.h>
+
+#define FAILIF(x, ...) do if (x) { \
+    fprintf(stderr, __VA_ARGS__);  \
+    exit(EXIT_FAILURE);            \
+} while (0)
+
+int
+main(int argc, char *argv[])
+{
+    int ifd, ofd, ofd_c;
+    int nr, nw;
+    int opt;
+//    struct tegra_audio_buf_config config;
+    char *name;
+    char *buffer;
+    int len = -1;
+//    struct tegra_audio_error_counts errors, errors_tot;
+
+    while ((opt = getopt(argc, argv, "n:")) != -1) {
+        switch (opt) {
+        case 'n':
+            len = atoi(optarg);
+            break;
+        default: /* '?' */
+            fprintf(stderr, "Usage: %s [-n<len>] name\n",
+                    argv[0]);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    name = argv[optind];
+    FAILIF(!name, "Expecting a file to play!\n");
+
+    printf("file to play: [%s]\n", name);
+
+    ifd = open(name, O_RDONLY);
+    FAILIF(ifd < 0, "could not open %s: %s\n", name, strerror(errno));
+
+    ofd = open("/dev/audio0_out", O_RDWR);
+    FAILIF(ofd < 0, "could not open output: %s\n", strerror(errno));
+
+    ofd_c = open("/dev/audio0_out_ctl", O_RDWR);
+    FAILIF(ofd_c < 0, "could not open output control: %s\n", strerror(errno));
+
+#if 0
+    FAILIF(ioctl(ofd_c, TEGRA_AUDIO_OUT_GET_BUF_CONFIG, &config) < 0,
+           "Could not get output config: %s\n", strerror(errno));
+#endif
+
+    if (len < 0)
+        len = 4096;
+
+    printf("write length: %d\n", len);
+
+    buffer = malloc(len);
+    FAILIF(!buffer, "Could not allocate %d bytes!\n", len);
+
+//    memset(&errors_tot, 0, sizeof(errors_tot));
+    do {
+        nr = read(ifd, buffer, len);
+        if (!nr) {
+            printf("EOF\n");
+            break;
+        }
+        FAILIF(nr < 0, "Could not read from %s: %s\n", name, strerror(errno));
+        nw = write(ofd, buffer, nr);
+        FAILIF(nw < 0, "Could not copy to output: %s\n", strerror(errno));
+        FAILIF(nw != nr, "Mismatch nw = %d nr = %d\n", nw, nr);
+
+#if 0
+        FAILIF(ioctl(ofd_c, TEGRA_AUDIO_OUT_GET_ERROR_COUNT, &errors) < 0,
+               "Could not get error count: %s\n", strerror(errno));
+
+        if (errors.late_dma || errors.full_empty) {
+            printf("out %d (%d late, %d underrun errors)\n", nw,
+                   errors.late_dma, errors.full_empty);
+            errors_tot.late_dma += errors.late_dma;
+            errors_tot.full_empty += errors.full_empty;
+        }
+        else
+#endif
+            printf("out %d\n", nw);
+
+    } while (1);
+
+#if 0
+    printf("played with %d late, %d underflow errors\n",
+           errors_tot.late_dma, errors_tot.full_empty);
+#endif
+    return 0;
+}
+
diff --git a/taudio/trec.c b/taudio/trec.c
new file mode 100644
index 0000000..2ad506d
--- /dev/null
+++ b/taudio/trec.c
@@ -0,0 +1,200 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdint.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <linux/cpcap_audio.h>
+#include <linux/tegra_audio.h>
+
+#define FAILIF(x, ...) do if (x) { \
+    fprintf(stderr, __VA_ARGS__);  \
+    exit(EXIT_FAILURE);            \
+} while (0)
+
+static char buffer[4096];
+
+struct wav_header {
+    char  riff[4];
+    uint32_t chunk_size;
+    char  format[4];
+
+    char  subchunk1_id[4];
+    uint32_t subchunk1_size;
+    uint16_t audio_format;
+    uint16_t num_channels;
+    uint32_t sample_rate;
+    uint32_t byte_rate;
+    uint16_t block_align;
+    uint16_t bits_per_sample;
+
+    char  subchunk2_id[4];
+    uint32_t subchunk2_size;
+} __attribute__((packed));
+
+static void init_wav_header(struct wav_header *hdr,
+                       uint32_t num_samples,
+                       uint16_t bits_per_sample,
+                       int   channels,
+                       uint32_t sample_rate)
+{
+    hdr->riff[0] = 'R';
+    hdr->riff[1] = 'I';
+    hdr->riff[2] = 'F';
+    hdr->riff[3] = 'F';
+
+    hdr->subchunk2_size = num_samples * channels * bits_per_sample / 8;
+
+    hdr->chunk_size = 36 + hdr->subchunk2_size;
+    hdr->format[0] = 'W';
+    hdr->format[1] = 'A';
+    hdr->format[2] = 'V';
+    hdr->format[3] = 'E';
+
+    hdr->subchunk1_id[0] = 'f';
+    hdr->subchunk1_id[1] = 'm';
+    hdr->subchunk1_id[2] = 't';
+    hdr->subchunk1_id[3] = ' ';
+
+    hdr->subchunk1_size = 16;
+    hdr->audio_format = 1; /* PCM */
+    hdr->num_channels = channels;
+    hdr->sample_rate = sample_rate;
+    hdr->byte_rate = sample_rate * channels * bits_per_sample / 8;
+    hdr->block_align = channels * bits_per_sample / 8;
+    hdr->bits_per_sample = bits_per_sample;
+
+    hdr->subchunk2_id[0] = 'd';
+    hdr->subchunk2_id[1] = 'a';
+    hdr->subchunk2_id[2] = 't';
+    hdr->subchunk2_id[3] = 'a';
+}
+
+int
+main(int argc, char *argv[])
+{
+    int ifd, ifd_c, ofd, opt, cfd;
+    const char *name;
+    int nr, nw = 0, total = 0;
+    int wave = 0;
+    const int bits_per_sample = 16;
+    int sampling_rate = -1;
+    int num_channels = -1;
+
+    struct tegra_audio_in_config cfg;
+    struct wav_header hdr;
+
+    while ((opt = getopt(argc, argv, "wc:s:")) != -1) {
+        switch (opt) {
+        case 'w':
+            wave = 1;
+            break;
+        case 'c':
+            num_channels = atoi(optarg);
+            assert(num_channels == 1 || num_channels == 2);
+            break;
+        case 's':
+            sampling_rate = atoi(optarg);
+            break;
+        default: /* '?' */
+            fprintf(stderr,
+                    "usage: %s [-w] [-s<rate>] [-c<chans>] <destfile>\n",
+                    *argv);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    FAILIF(optind >= argc,
+                    "usage: %s [-w] [-s<rate>] [-c<chans>] <destfile>\n",
+                    *argv);
+
+    name = argv[optind];
+
+    printf("> recording into %s\n", name);
+    printf("> sampling rate %d\n", sampling_rate);
+    printf("> channels %d\n", num_channels);
+
+    cfd = open("/dev/audio_ctl", O_RDWR);
+    FAILIF(cfd < 0, "could not open control: %s\n", strerror(errno));
+    if(sampling_rate > 0) {
+        FAILIF(ioctl(cfd, CPCAP_AUDIO_IN_SET_RATE, sampling_rate),
+            "Could not set input sampling rate: %s\n", strerror(errno));
+    }
+
+    ifd = open("/dev/audio1_in", O_RDWR);
+    FAILIF(ifd < 0, "could not open input: %s\n", strerror(errno));
+
+    ifd_c = open("/dev/audio1_in_ctl", O_RDWR);
+    FAILIF(ifd < 0, "could not open input: %s\n", strerror(errno));
+
+    printf("getting audio-input config\n");
+    FAILIF(ioctl(ifd_c, TEGRA_AUDIO_IN_GET_CONFIG, &cfg) < 0,
+           "could not get input config: %s\n", strerror(errno));
+
+    if (num_channels >= 0 || sampling_rate >= 0) {
+        if (num_channels >= 0)
+            cfg.stereo = num_channels == 2;
+//        if (sampling_rate >= 0)
+//            cfg.rate = sampling_rate;
+//  No sample rate conversion in driver
+        cfg.rate = 44100;
+        printf("setting audio-input config (stereo %d, rate %d)\n",
+               cfg.stereo, cfg.rate);
+        FAILIF(ioctl(ifd_c, TEGRA_AUDIO_IN_SET_CONFIG, &cfg) < 0,
+               "could not set input config: %s\n", strerror(errno));
+    }
+
+    if (num_channels < 0) {
+        num_channels = cfg.stereo ? 2 : 1;
+        printf("> channels %d (from config)\n", num_channels);
+    }
+
+    if (sampling_rate < 0) {
+        sampling_rate = cfg.rate;
+        printf("> sampling rate %d (from config)\n", sampling_rate);
+    }
+
+    ofd = open(name, O_WRONLY | O_TRUNC | O_CREAT, 0666);
+    FAILIF(ofd < 0, "could not open %s: %s\n", name, strerror(errno));
+
+    if (wave)
+        FAILIF(lseek(ofd, sizeof(struct wav_header), SEEK_SET) < 0,
+               "seek error: %s\n", strerror(errno));
+
+    do {
+        errno = 0;
+        nr = read(ifd, buffer, sizeof(buffer));
+        FAILIF(nr < 0, "input read error: %s\n", strerror(errno));
+
+        if (!nr) {
+            printf("done recording\n");
+            break;
+        }
+
+        printf("in %d\n", nr);
+
+        nw = write(ofd, buffer, nr);
+        FAILIF(nw < 0, "Could not copy to output: %s\n", strerror(errno));
+        FAILIF(nw != nr, "Mismatch nw = %d nr = %d\n", nw, nr);
+        total += nw;
+    } while (1);
+
+    if (wave) {
+        printf("writing WAV header\n");
+        lseek(ofd, 0, SEEK_SET);
+        init_wav_header(&hdr,
+                        total * 8 / (num_channels * bits_per_sample),
+                        bits_per_sample,
+                        num_channels,
+                        sampling_rate);
+        FAILIF(write(ofd, &hdr, sizeof(hdr)) != sizeof(hdr),
+               "Could not write WAV header: %s\n", strerror(errno));
+    }
+
+    printf("done\n");
+    return 0;
+}
diff --git a/taudio/twav.c b/taudio/twav.c
new file mode 100644
index 0000000..f57792b
--- /dev/null
+++ b/taudio/twav.c
@@ -0,0 +1,163 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdint.h>
+#include <assert.h>
+#include <string.h>
+
+#include <sys/ioctl.h>
+
+#define FAILIF(x, ...) do if (x) { \
+    fprintf(stderr, __VA_ARGS__);  \
+    exit(EXIT_FAILURE);            \
+} while (0)
+
+static char buffer[4096];
+
+struct wav_header {
+    char  riff[4];
+    uint32_t chunk_size;
+    char  format[4];
+
+    char  subchunk1_id[4];
+    uint32_t subchunk1_size;
+    uint16_t audio_format;
+    uint16_t num_channels;
+    uint32_t sample_rate;
+    uint32_t byte_rate;
+    uint16_t block_align;
+    uint16_t bits_per_sample;
+
+    char  subchunk2_id[4];
+    uint32_t subchunk2_size;
+} __attribute__((packed));
+
+static void init_wav_header(struct wav_header *hdr,
+                       uint32_t num_samples,
+                       uint16_t bits_per_sample,
+                       int   channels,
+                       uint32_t sample_rate)
+{
+    hdr->riff[0] = 'R';
+    hdr->riff[1] = 'I';
+    hdr->riff[2] = 'F';
+    hdr->riff[3] = 'F';
+
+    hdr->subchunk2_size = num_samples * channels * bits_per_sample / 8;
+
+    hdr->chunk_size = 36 + hdr->subchunk2_size;
+    hdr->format[0] = 'W';
+    hdr->format[1] = 'A';
+    hdr->format[2] = 'V';
+    hdr->format[3] = 'E';
+
+    hdr->subchunk1_id[0] = 'f';
+    hdr->subchunk1_id[1] = 'm';
+    hdr->subchunk1_id[2] = 't';
+    hdr->subchunk1_id[3] = ' ';
+
+    hdr->subchunk1_size = 16;
+    hdr->audio_format = 1; /* PCM */
+    hdr->num_channels = channels;
+    hdr->sample_rate = sample_rate;
+    hdr->byte_rate = sample_rate * channels * bits_per_sample / 8;
+    hdr->block_align = channels * bits_per_sample / 8;
+    hdr->bits_per_sample = bits_per_sample;
+
+    hdr->subchunk2_id[0] = 'd';
+    hdr->subchunk2_id[1] = 'a';
+    hdr->subchunk2_id[2] = 't';
+    hdr->subchunk2_id[3] = 'a';
+}
+
+int
+main(int argc, char *argv[])
+{
+    int ifd, ofd;
+    int nr, nw = 0, total = 0;
+    struct wav_header hdr;
+
+    int opt;
+    char * output = NULL;
+    char * input = NULL;
+    int bits_per_sample = 16;
+    int sampling_rate = -1;
+    int num_channels = -1;
+
+    while ((opt = getopt(argc, argv, "o:c:b:s:")) != -1) {
+        switch (opt) {
+        case 'o':
+            output = strdup(optarg);
+            break;
+        case 'c':
+            num_channels = atoi(optarg);
+            assert(num_channels == 1 || num_channels == 2);
+            break;
+        case 'b':
+            bits_per_sample = atoi(optarg);
+            break;
+        case 's':
+            sampling_rate = atoi(optarg);
+            break;
+        default: /* '?' */
+            fprintf(stderr, "Usage: %s [-ooutfile] -c2 -b16 -s44100 infile\n",
+                    argv[0]);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    assert(sampling_rate >= 0);
+    assert(num_channels >= 0);
+
+    if (optind >= argc) {
+          fprintf(stderr, "Expected argument after options\n");
+          exit(EXIT_FAILURE);
+    }
+
+    input = argv[optind];
+
+    printf("> input %s\n", input);
+    printf("> output %s\n", input);
+    printf("> bits per sample %d\n", bits_per_sample);
+    printf("> sampling rate %d\n", sampling_rate);
+    printf("> channels %d\n", num_channels);
+
+    ofd = open(output, O_WRONLY | O_CREAT, 0777);
+    FAILIF(ofd < 0, "could not open %s: %s\n", output, strerror(errno));
+
+    ifd = open(input, O_RDONLY);
+    FAILIF(ifd < 0, "could not open %s: %s\n", input, strerror(errno));
+
+    lseek(ofd, sizeof(struct wav_header), SEEK_SET);
+
+    do {
+        nr = read(ifd, buffer, sizeof(buffer));
+        FAILIF(nr < 0, "Could not read from input: %s\n", strerror(errno));
+        if (!nr) {
+            printf("done recording\n");
+            break;
+        }
+        nw = write(ofd, buffer, nr);
+        FAILIF(nw < 0, "Could not copy to output: %s\n", strerror(errno));
+        FAILIF(nw != nr, "Mismatch nw = %d nr = %d\n", nw, nr);
+        total += nw;
+    } while (1);
+
+    printf("writing WAV header\n");
+    lseek(ofd, 0, SEEK_SET);
+    init_wav_header(&hdr,
+		    total * 8 / (num_channels * bits_per_sample),
+		    bits_per_sample,
+		    num_channels,
+		    sampling_rate);
+
+    FAILIF(write(ofd, &hdr, sizeof(hdr)) != sizeof(hdr),
+           "Could not write WAV header: %s\n", strerror(errno));
+
+    printf("done\n");
+    return 0;
+}
diff --git a/ueventd.stingray.rc b/ueventd.stingray.rc
new file mode 100644
index 0000000..116a449
--- /dev/null
+++ b/ueventd.stingray.rc
@@ -0,0 +1,20 @@
+/dev/knvmap	0660	system	system
+/dev/nvmap	0666	system	system
+/dev/ov5650	0660	media	camera
+/dev/dw9714l    0660    media   camera
+/dev/soc2030	0660	media	camera
+/dev/audio*	0660	system	audio
+/dev/spdif*	0660	system	audio
+/dev/tegra_camera	0660	media	camera
+/dev/ttyUSB0	0640	radio	radio
+/dev/ttyUSB1	0640	radio	radio
+/dev/ttyUSB2	0640	radio	radio
+/dev/ttyUSB3	0640	radio	radio
+/dev/ttyUSB4	0640	radio	radio
+/dev/ttyUSB5	0640	radio	radio
+/dev/ttyUSB6	0640	radio	radio
+/dev/cpcap	0600	radio	radio
+/dev/tegra_avp	0660	media	media
+/dev/tegra_rpc	0660	media	media
+/dev/tegra_sema	0660	media	media
+/dev/tegra-crypto	0660	system	system
diff --git a/vold.fstab b/vold.fstab
new file mode 100644
index 0000000..9c4cdec
--- /dev/null
+++ b/vold.fstab
@@ -0,0 +1,13 @@
+## Vold 2.0 fstab for Stingray
+
+#######################
+## Regular device mount
+##
+## Format: dev_mount <label> <mount_point> <part> <sysfs_path1...>
+## label        - Label for the volume
+## mount_point  - Where the volume will be mounted
+## part         - Partition # (1 based), or 'auto' for first usable partition.
+## <sysfs_path> - List of sysfs paths to source devices
+######################
+
+# No SD card - nothing to do here