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