summaryrefslogtreecommitdiff
path: root/cras
diff options
context:
space:
mode:
Diffstat (limited to 'cras')
-rw-r--r--cras/README.dbus-api4
-rw-r--r--cras/client/cras-sys/src/gen.rs88
-rw-r--r--cras/client/cras-sys/src/lib.rs15
-rw-r--r--cras/client/cras_tests/src/audio.rs2
-rw-r--r--cras/client/libcras/src/cras_stream.rs16
-rw-r--r--cras/client/libcras/src/libcras.rs2
-rw-r--r--cras/src/Makefile.am17
-rw-r--r--cras/src/common/cras_file_wait.c2
-rw-r--r--cras/src/common/cras_types.h13
-rw-r--r--cras/src/common/cras_util.h13
-rw-r--r--cras/src/dsp/drc.c2
-rw-r--r--cras/src/dsp/drc_kernel.c24
-rw-r--r--cras/src/dsp/drc_kernel.h2
-rw-r--r--cras/src/libcras/cras_client.c444
-rw-r--r--cras/src/libcras/cras_client.h693
-rw-r--r--cras/src/server/audio_thread.c3
-rw-r--r--cras/src/server/config/cras_board_config.c7
-rw-r--r--cras/src/server/config/cras_board_config.h1
-rw-r--r--cras/src/server/cras_a2dp_iodev.c6
-rw-r--r--cras/src/server/cras_alsa_io.c43
-rw-r--r--cras/src/server/cras_alsa_mixer.c2
-rw-r--r--cras/src/server/cras_alsa_mixer.h2
-rw-r--r--cras/src/server/cras_alsa_ucm.c155
-rw-r--r--cras/src/server/cras_alsa_ucm.h37
-rw-r--r--cras/src/server/cras_apm_list.c32
-rw-r--r--cras/src/server/cras_bt_battery_provider.c371
-rw-r--r--cras/src/server/cras_bt_battery_provider.h47
-rw-r--r--cras/src/server/cras_bt_constants.h10
-rw-r--r--cras/src/server/cras_bt_device.c19
-rw-r--r--cras/src/server/cras_bt_device.h4
-rw-r--r--cras/src/server/cras_bt_io.c5
-rw-r--r--cras/src/server/cras_bt_manager.c31
-rw-r--r--cras/src/server/cras_dbus_control.c65
-rw-r--r--cras/src/server/cras_device_monitor.c20
-rw-r--r--cras/src/server/cras_device_monitor.h4
-rw-r--r--cras/src/server/cras_fmt_conv.c15
-rw-r--r--cras/src/server/cras_fmt_conv_ops.c38
-rw-r--r--cras/src/server/cras_fmt_conv_ops.h7
-rw-r--r--cras/src/server/cras_hfp_ag_profile.c14
-rw-r--r--cras/src/server/cras_hfp_ag_profile.h4
-rw-r--r--cras/src/server/cras_hfp_alsa_iodev.c8
-rw-r--r--cras/src/server/cras_hfp_iodev.c11
-rw-r--r--cras/src/server/cras_hfp_slc.c101
-rw-r--r--cras/src/server/cras_hfp_slc.h5
-rw-r--r--cras/src/server/cras_iodev.c22
-rw-r--r--cras/src/server/cras_iodev.h14
-rw-r--r--cras/src/server/cras_iodev_list.c45
-rw-r--r--cras/src/server/cras_iodev_list.h5
-rw-r--r--cras/src/server/cras_rclient_util.c2
-rw-r--r--cras/src/server/cras_rstream.c29
-rw-r--r--cras/src/server/cras_rstream.h12
-rw-r--r--cras/src/server/cras_server_metrics.c229
-rw-r--r--cras/src/server/cras_server_metrics.h6
-rw-r--r--cras/src/server/cras_system_state.c30
-rw-r--r--cras/src/server/cras_system_state.h12
-rw-r--r--cras/src/server/dev_io.c274
-rw-r--r--cras/src/server/dev_io.h5
-rw-r--r--cras/src/server/dev_stream.c23
-rw-r--r--cras/src/server/dev_stream.h27
-rw-r--r--cras/src/server/server_stream.c3
-rw-r--r--cras/src/server/stream_list.c32
-rw-r--r--cras/src/server/stream_list.h11
-rw-r--r--cras/src/tests/a2dp_iodev_unittest.cc4
-rw-r--r--cras/src/tests/alsa_io_unittest.cc21
-rw-r--r--cras/src/tests/alsa_mixer_unittest.cc26
-rw-r--r--cras/src/tests/alsa_ucm_unittest.cc157
-rw-r--r--cras/src/tests/apm_list_unittest.cc61
-rw-r--r--cras/src/tests/audio_thread_unittest.cc14
-rw-r--r--cras/src/tests/bt_io_unittest.cc4
-rw-r--r--cras/src/tests/capture_rclient_unittest.cc5
-rw-r--r--cras/src/tests/control_rclient_unittest.cc7
-rw-r--r--cras/src/tests/cras_abi_unittest.cc139
-rw-r--r--cras/src/tests/dev_io_stubs.cc5
-rw-r--r--cras/src/tests/dev_io_unittest.cc94
-rw-r--r--cras/src/tests/dev_stream_unittest.cc76
-rw-r--r--cras/src/tests/fmt_conv_ops_unittest.cc33
-rw-r--r--cras/src/tests/hfp_alsa_iodev_unittest.cc8
-rw-r--r--cras/src/tests/hfp_iodev_unittest.cc4
-rw-r--r--cras/src/tests/iodev_list_unittest.cc113
-rw-r--r--cras/src/tests/iodev_stub.cc36
-rw-r--r--cras/src/tests/iodev_stub.h6
-rw-r--r--cras/src/tests/iodev_unittest.cc14
-rw-r--r--cras/src/tests/playback_rclient_unittest.cc5
-rw-r--r--cras/src/tests/rstream_unittest.cc1
-rw-r--r--cras/src/tests/server_metrics_unittest.cc37
-rw-r--r--cras/src/tests/stream_list_unittest.cc66
-rw-r--r--cras/src/tests/system_state_unittest.cc40
-rw-r--r--cras/src/tests/timing_unittest.cc78
-rw-r--r--cras/src/tools/cras_test_client/cras_test_client.c11
89 files changed, 3797 insertions, 453 deletions
diff --git a/cras/README.dbus-api b/cras/README.dbus-api
index c55a8df6..f347358e 100644
--- a/cras/README.dbus-api
+++ b/cras/README.dbus-api
@@ -236,7 +236,3 @@ Signals OutputVolumeChanged(int32 volume)
HotwordTriggered(int64 tv_sec, int64 tv_nsec)
Indicates that hotword was triggered at the given timestamp.
-
- BluetoothBatteryChanged(string address, uint32 level)
-
- Indicates the battery level of a bluetooth device changed.
diff --git a/cras/client/cras-sys/src/gen.rs b/cras/client/cras-sys/src/gen.rs
index 0375a0bd..6fb4cdf8 100644
--- a/cras/client/cras-sys/src/gen.rs
+++ b/cras/client/cras-sys/src/gen.rs
@@ -748,7 +748,9 @@ pub enum CRAS_CONNECTION_TYPE {
CRAS_CAPTURE = 2,
CRAS_VMS_LEGACY = 3,
CRAS_VMS_UNIFIED = 4,
- CRAS_NUM_CONN_TYPE = 5,
+ CRAS_PLUGIN_PLAYBACK = 5,
+ CRAS_PLUGIN_UNIFIED = 6,
+ CRAS_NUM_CONN_TYPE = 7,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -798,6 +800,9 @@ pub enum CRAS_CLIENT_TYPE {
CRAS_CLIENT_TYPE_CROSVM = 6,
CRAS_CLIENT_TYPE_SERVER_STREAM = 7,
CRAS_CLIENT_TYPE_LACROS = 8,
+ CRAS_CLIENT_TYPE_PLUGIN = 9,
+ CRAS_CLIENT_TYPE_ARCVM = 10,
+ CRAS_NUM_CLIENT_TYPE = 11,
}
impl CRAS_STREAM_EFFECT {
pub const APM_ECHO_CANCELLATION: CRAS_STREAM_EFFECT = CRAS_STREAM_EFFECT(1);
@@ -914,24 +919,25 @@ pub enum CRAS_BT_LOG_EVENTS {
BT_A2DP_START = 6,
BT_A2DP_SUSPENDED = 7,
BT_CODEC_SELECTION = 8,
- BT_DEV_CONNECTED_CHANGE = 9,
- BT_DEV_CONN_WATCH_CB = 10,
- BT_DEV_SUSPEND_CB = 11,
- BT_HFP_NEW_CONNECTION = 12,
- BT_HFP_REQUEST_DISCONNECT = 13,
- BT_HFP_SUPPORTED_FEATURES = 14,
- BT_HFP_HF_INDICATOR = 15,
- BT_HFP_SET_SPEAKER_GAIN = 16,
- BT_HFP_UPDATE_SPEAKER_GAIN = 17,
- BT_HSP_NEW_CONNECTION = 18,
- BT_HSP_REQUEST_DISCONNECT = 19,
- BT_NEW_AUDIO_PROFILE_AFTER_CONNECT = 20,
- BT_RESET = 21,
- BT_SCO_CONNECT = 22,
- BT_TRANSPORT_ACQUIRE = 23,
- BT_TRANSPORT_RELEASE = 24,
- BT_TRANSPORT_SET_VOLUME = 25,
- BT_TRANSPORT_UPDATE_VOLUME = 26,
+ BT_DEV_CONNECTED = 9,
+ BT_DEV_DISCONNECTED = 10,
+ BT_DEV_CONN_WATCH_CB = 11,
+ BT_DEV_SUSPEND_CB = 12,
+ BT_HFP_NEW_CONNECTION = 13,
+ BT_HFP_REQUEST_DISCONNECT = 14,
+ BT_HFP_SUPPORTED_FEATURES = 15,
+ BT_HFP_HF_INDICATOR = 16,
+ BT_HFP_SET_SPEAKER_GAIN = 17,
+ BT_HFP_UPDATE_SPEAKER_GAIN = 18,
+ BT_HSP_NEW_CONNECTION = 19,
+ BT_HSP_REQUEST_DISCONNECT = 20,
+ BT_NEW_AUDIO_PROFILE_AFTER_CONNECT = 21,
+ BT_RESET = 22,
+ BT_SCO_CONNECT = 23,
+ BT_TRANSPORT_ACQUIRE = 24,
+ BT_TRANSPORT_RELEASE = 25,
+ BT_TRANSPORT_SET_VOLUME = 26,
+ BT_TRANSPORT_UPDATE_VOLUME = 27,
}
#[repr(C, packed)]
#[derive(Debug, Copy, Clone)]
@@ -2117,12 +2123,15 @@ pub struct cras_server_state {
pub bt_wbs_enabled: i32,
pub deprioritize_bt_wbs_mic: i32,
pub main_thread_debug_info: main_thread_debug_info,
+ pub num_input_streams_with_permission: [u32; 11usize],
+ pub noise_cancellation_enabled: i32,
+ pub hotword_pause_at_suspend: i32,
}
#[test]
fn bindgen_test_layout_cras_server_state() {
assert_eq!(
::std::mem::size_of::<cras_server_state>(),
- 1414292usize,
+ 1414344usize,
concat!("Size of: ", stringify!(cras_server_state))
);
assert_eq!(
@@ -2520,6 +2529,45 @@ fn bindgen_test_layout_cras_server_state() {
stringify!(main_thread_debug_info)
)
);
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<cras_server_state>())).num_input_streams_with_permission
+ as *const _ as usize
+ },
+ 1414292usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(cras_server_state),
+ "::",
+ stringify!(num_input_streams_with_permission)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<cras_server_state>())).noise_cancellation_enabled as *const _
+ as usize
+ },
+ 1414336usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(cras_server_state),
+ "::",
+ stringify!(noise_cancellation_enabled)
+ )
+ );
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<cras_server_state>())).hotword_pause_at_suspend as *const _
+ as usize
+ },
+ 1414340usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(cras_server_state),
+ "::",
+ stringify!(hotword_pause_at_suspend)
+ )
+ );
}
pub const cras_notify_device_action_CRAS_DEVICE_ACTION_ADD: cras_notify_device_action = 0;
pub const cras_notify_device_action_CRAS_DEVICE_ACTION_REMOVE: cras_notify_device_action = 1;
diff --git a/cras/client/cras-sys/src/lib.rs b/cras/client/cras-sys/src/lib.rs
index 8128575b..2b3d21e0 100644
--- a/cras/client/cras-sys/src/lib.rs
+++ b/cras/client/cras-sys/src/lib.rs
@@ -10,6 +10,7 @@ use std::error;
use std::fmt;
use std::iter::FromIterator;
use std::os::raw::c_char;
+use std::str::FromStr;
use std::time::Duration;
#[allow(dead_code)]
@@ -47,6 +48,7 @@ unsafe impl data_model::DataInit for gen::cras_set_system_volume {}
pub enum Error {
InvalidChannel(i8),
InvalidClientType(u32),
+ InvalidClientTypeStr,
InvalidStreamType(u32),
}
@@ -68,6 +70,7 @@ impl fmt::Display for Error {
t,
CRAS_CLIENT_TYPE::CRAS_CLIENT_TYPE_SERVER_STREAM as u32 + 1
),
+ InvalidClientTypeStr => write!(f, "Invalid client type string"),
InvalidStreamType(t) => write!(
f,
"Stream type {} is not within valid range [0, {})",
@@ -426,6 +429,18 @@ impl TryFrom<u32> for CRAS_CLIENT_TYPE {
}
}
+impl FromStr for CRAS_CLIENT_TYPE {
+ type Err = Error;
+ fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+ use CRAS_CLIENT_TYPE::*;
+ match s {
+ "crosvm" => Ok(CRAS_CLIENT_TYPE_CROSVM),
+ "arcvm" => Ok(CRAS_CLIENT_TYPE_ARCVM),
+ _ => Err(Error::InvalidClientTypeStr),
+ }
+ }
+}
+
impl Default for audio_stream_debug_info {
fn default() -> Self {
Self {
diff --git a/cras/client/cras_tests/src/audio.rs b/cras/client/cras_tests/src/audio.rs
index 5ab22474..23018fd7 100644
--- a/cras/client/cras_tests/src/audio.rs
+++ b/cras/client/cras_tests/src/audio.rs
@@ -59,7 +59,7 @@ type Result<T> = std::result::Result<T, Error>;
static INTERRUPTED: AtomicBool = AtomicBool::new(false);
-extern "C" fn sigint_handler() {
+extern "C" fn sigint_handler(_: c_int) {
// Check if we've already received one SIGINT. If we have, the program may
// be misbehaving and not terminating, so to be safe we'll forcefully exit.
if INTERRUPTED.load(Ordering::Acquire) {
diff --git a/cras/client/libcras/src/cras_stream.rs b/cras/client/libcras/src/cras_stream.rs
index 5914bfdd..f6004802 100644
--- a/cras/client/libcras/src/cras_stream.rs
+++ b/cras/client/libcras/src/cras_stream.rs
@@ -165,20 +165,20 @@ impl<'a, T: CrasStreamData<'a> + BufferDrop> CrasStream<'a, T> {
fn wait_request_data(&mut self) -> Result<(), Error> {
match self.controls.audio_sock_mut().read_audio_message()? {
- AudioMessage::Success { id, .. } => match id {
- CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_REQUEST_DATA => Ok(()),
- _ => Err(Error::MessageTypeError),
- },
+ AudioMessage::Success {
+ id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_REQUEST_DATA,
+ ..
+ } => Ok(()),
_ => Err(Error::MessageTypeError),
}
}
fn wait_data_ready(&mut self) -> Result<u32, Error> {
match self.controls.audio_sock_mut().read_audio_message()? {
- AudioMessage::Success { id, frames } => match id {
- CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_DATA_READY => Ok(frames),
- _ => Err(Error::MessageTypeError),
- },
+ AudioMessage::Success {
+ id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_DATA_READY,
+ frames,
+ } => Ok(frames),
_ => Err(Error::MessageTypeError),
}
}
diff --git a/cras/client/libcras/src/libcras.rs b/cras/client/libcras/src/libcras.rs
index 80d2cff7..402a4a27 100644
--- a/cras/client/libcras/src/libcras.rs
+++ b/cras/client/libcras/src/libcras.rs
@@ -136,7 +136,7 @@ pub use cras_sys::gen::{
CRAS_CLIENT_TYPE as CrasClientType, CRAS_NODE_TYPE as CrasNodeType,
CRAS_STREAM_EFFECT as CrasStreamEffect,
};
-pub use cras_sys::{AudioDebugInfo, CrasIodevInfo, CrasIonodeInfo};
+pub use cras_sys::{AudioDebugInfo, CrasIodevInfo, CrasIonodeInfo, Error as CrasSysError};
use sys_util::{PollContext, PollToken, SharedMemory};
mod audio_socket;
diff --git a/cras/src/Makefile.am b/cras/src/Makefile.am
index 69fea5ff..1e89f811 100644
--- a/cras/src/Makefile.am
+++ b/cras/src/Makefile.am
@@ -55,6 +55,7 @@ CRAS_DBUS_SOURCES = \
server/cras_bt_player.c \
server/cras_bt_io.c \
server/cras_bt_profile.c \
+ server/cras_bt_battery_provider.c \
server/cras_dbus.c \
server/cras_dbus_util.c \
server/cras_dbus_control.c \
@@ -426,6 +427,7 @@ TESTS = \
byte_buffer_unittest \
card_config_unittest \
checksum_unittest \
+ cras_abi_unittest \
cras_client_unittest \
cras_tm_unittest \
device_monitor_unittest \
@@ -613,7 +615,7 @@ a2dp_info_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/server \
-I$(top_srcdir)/src/common
a2dp_info_unittest_LDADD = -lgtest -lpthread
-a2dp_iodev_unittest_SOURCES = tests/a2dp_iodev_unittest.cc common/sfh.c
+a2dp_iodev_unittest_SOURCES = tests/a2dp_iodev_unittest.cc
a2dp_iodev_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/server \
-I$(top_srcdir)/src/common $(DBUS_CFLAGS)
a2dp_iodev_unittest_LDADD = -lgtest -lpthread $(DBUS_LIBS)
@@ -686,7 +688,7 @@ audio_thread_monitor_unittest_LDADD = -lgtest -lpthread -lrt
if HAVE_DBUS
bt_device_unittest_SOURCES = tests/bt_device_unittest.cc \
server/cras_bt_device.c \
- tests/metrics_stub.cc
+ tests/metrics_stub.cc common/sfh.c
bt_device_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/server \
-I$(top_srcdir)/src/common $(DBUS_CFLAGS)
bt_device_unittest_LDADD = -lgtest -lpthread $(DBUS_LIBS)
@@ -718,6 +720,13 @@ checksum_unittest_SOURCES = tests/checksum_unittest.cc common/cras_checksum.c
checksum_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common
checksum_unittest_LDADD = -lgtest -lpthread
+cras_abi_unittest_SOURCES = tests/cras_abi_unittest.cc \
+ common/cras_config.c common/cras_shm.c common/cras_util.c \
+ common/cras_file_wait.c common/cras_audio_format.c
+cras_abi_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/libcras
+cras_abi_unittest_LDADD = -lgtest -lpthread -lrt -lspeexdsp
+
cras_client_unittest_SOURCES = tests/cras_client_unittest.cc \
common/cras_config.c common/cras_shm.c common/cras_util.c \
common/cras_file_wait.c
@@ -854,13 +863,13 @@ hfp_info_unittest_LDADD = -lgtest -lpthread
if HAVE_DBUS
hfp_iodev_unittest_SOURCES = tests/hfp_iodev_unittest.cc \
- server/cras_hfp_iodev.c common/sfh.c
+ server/cras_hfp_iodev.c
hfp_iodev_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/server $(DBUS_CFLAGS)
hfp_iodev_unittest_LDADD = -lgtest -lpthread $(DBUS_LIBS)
hfp_alsa_iodev_unittest_SOURCES = tests/hfp_alsa_iodev_unittest.cc \
- server/cras_hfp_alsa_iodev.c common/sfh.c
+ server/cras_hfp_alsa_iodev.c
hfp_alsa_iodev_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
-I$(top_srcdir)/src/common -I$(top_srcdir)/src/server $(DBUS_CFLAGS)
hfp_alsa_iodev_unittest_LDADD = -lgtest -lpthread $(DBUS_LIBS)
diff --git a/cras/src/common/cras_file_wait.c b/cras/src/common/cras_file_wait.c
index 9ad94486..190a5e10 100644
--- a/cras/src/common/cras_file_wait.c
+++ b/cras/src/common/cras_file_wait.c
@@ -190,7 +190,7 @@ int cras_file_wait_dispatch(struct cras_file_wait *file_wait)
strcpy(file_wait->watch_dir, file_wait->file_path);
watch_dir_len = file_wait->file_path_len;
- while (rc == -ENOENT) {
+ while (rc == -ENOENT || rc == -EACCES) {
strcpy(file_wait->watch_path, file_wait->watch_dir);
watch_path_len = watch_dir_len;
diff --git a/cras/src/common/cras_types.h b/cras/src/common/cras_types.h
index 90a04741..544ba02c 100644
--- a/cras/src/common/cras_types.h
+++ b/cras/src/common/cras_types.h
@@ -169,6 +169,7 @@ enum CRAS_CLIENT_TYPE {
CRAS_CLIENT_TYPE_SERVER_STREAM, /* Server stream */
CRAS_CLIENT_TYPE_LACROS, /* LaCrOS */
CRAS_CLIENT_TYPE_PLUGIN, /* PluginVM */
+ CRAS_CLIENT_TYPE_ARCVM, /* ARCVM */
CRAS_NUM_CLIENT_TYPE, /* numbers of CRAS_CLIENT_TYPE */
};
@@ -213,6 +214,7 @@ cras_client_type_str(enum CRAS_CLIENT_TYPE client_type)
ENUM_STR(CRAS_CLIENT_TYPE_SERVER_STREAM)
ENUM_STR(CRAS_CLIENT_TYPE_LACROS)
ENUM_STR(CRAS_CLIENT_TYPE_PLUGIN)
+ ENUM_STR(CRAS_CLIENT_TYPE_ARCVM)
default:
return "INVALID_CLIENT_TYPE";
}
@@ -368,7 +370,8 @@ enum CRAS_BT_LOG_EVENTS {
BT_A2DP_START,
BT_A2DP_SUSPENDED,
BT_CODEC_SELECTION,
- BT_DEV_CONNECTED_CHANGE,
+ BT_DEV_CONNECTED,
+ BT_DEV_DISCONNECTED,
BT_DEV_CONN_WATCH_CB,
BT_DEV_SUSPEND_CB,
BT_HFP_NEW_CONNECTION,
@@ -573,6 +576,12 @@ struct __attribute__((__packed__)) cras_audio_thread_snapshot_buffer {
* main_thread_debug_info - ring buffer for storing main thread event logs.
* num_input_streams_with_permission - An array containing numbers of input
* streams with permission in each client type.
+ * noise_cancellation_enabled - Whether or not Noise Cancellation is enabled.
+ * hotword_pause_at_suspend - 1 = Pause hotword detection when the system
+ * suspends. Hotword detection is resumed after system resumes.
+ * 0 - Hotword detection is allowed to continue running after system
+ * suspends, so a detected hotword can wake up the device.
+ *
*/
#define CRAS_SERVER_STATE_VERSION 2
struct __attribute__((packed, aligned(4))) cras_server_state {
@@ -612,6 +621,8 @@ struct __attribute__((packed, aligned(4))) cras_server_state {
int32_t deprioritize_bt_wbs_mic;
struct main_thread_debug_info main_thread_debug_info;
uint32_t num_input_streams_with_permission[CRAS_NUM_CLIENT_TYPE];
+ int32_t noise_cancellation_enabled;
+ int32_t hotword_pause_at_suspend;
};
/* Actions for card add/remove/change. */
diff --git a/cras/src/common/cras_util.h b/cras/src/common/cras_util.h
index ed476b7e..96985ab2 100644
--- a/cras/src/common/cras_util.h
+++ b/cras/src/common/cras_util.h
@@ -201,6 +201,19 @@ static inline uint64_t cras_frames_until_time(const struct timespec *end,
return cras_time_to_frames(&time_until, rate);
}
+/* Returns true if the difference between a and b is shorter than t. */
+static inline bool timespec_diff_shorter_than(const struct timespec *a,
+ const struct timespec *b,
+ const struct timespec *t)
+{
+ struct timespec diff;
+ if (timespec_after(a, b))
+ subtract_timespecs(a, b, &diff);
+ else
+ subtract_timespecs(b, a, &diff);
+ return timespec_after(t, &diff);
+}
+
/* Poll on the given file descriptors.
*
* See ppoll(). This implementation changes the value of timeout to the
diff --git a/cras/src/dsp/drc.c b/cras/src/dsp/drc.c
index 1b2639a0..e6098419 100644
--- a/cras/src/dsp/drc.c
+++ b/cras/src/dsp/drc.c
@@ -104,7 +104,7 @@ static void set_default_parameters(struct drc *drc)
param[PARAM_RELEASE_ZONE3] = 0.42f;
param[PARAM_RELEASE_ZONE4] = 0.98f;
- /* This is effectively a master volume on the compressed
+ /* This is effectively a main volume on the compressed
* signal */
param[PARAM_POST_GAIN] = 0; /* dB */
param[PARAM_ENABLED] = 0;
diff --git a/cras/src/dsp/drc_kernel.c b/cras/src/dsp/drc_kernel.c
index c0eb100b..8c3404fc 100644
--- a/cras/src/dsp/drc_kernel.c
+++ b/cras/src/dsp/drc_kernel.c
@@ -257,7 +257,7 @@ void dk_set_parameters(struct drc_kernel *dk, float db_threshold, float db_knee,
/* Empirical/perceptual tuning. */
full_range_makeup_gain = powf(full_range_makeup_gain, 0.6f);
- dk->master_linear_gain =
+ dk->main_linear_gain =
decibels_to_linear(db_post_gain) * full_range_makeup_gain;
/* Attack parameters. */
@@ -566,7 +566,7 @@ static void dk_update_detector_average(struct drc_kernel *dk)
#include <arm_neon.h>
static void dk_compress_output(struct drc_kernel *dk)
{
- const float master_linear_gain = dk->master_linear_gain;
+ const float main_linear_gain = dk->main_linear_gain;
const float envelope_rate = dk->envelope_rate;
const float scaled_desired_gain = dk->scaled_desired_gain;
const float compressor_gain = dk->compressor_gain;
@@ -638,7 +638,7 @@ static void dk_compress_output(struct drc_kernel *dk)
[A7]"w"(A7),
[base]"w"(vdupq_n_f32(scaled_desired_gain)),
[r4]"w"(vdupq_n_f32(r*r*r*r)),
- [g]"w"(vdupq_n_f32(master_linear_gain))
+ [g]"w"(vdupq_n_f32(main_linear_gain))
: /* clobber */
"memory", "cc");
// clang-format on
@@ -698,7 +698,7 @@ static void dk_compress_output(struct drc_kernel *dk)
[A7]"w"(A7),
[one]"w"(vdupq_n_f32(1)),
[r4]"w"(vdupq_n_f32(r*r*r*r)),
- [g]"w"(vdupq_n_f32(master_linear_gain))
+ [g]"w"(vdupq_n_f32(main_linear_gain))
: /* clobber */
"memory", "cc");
// clang-format on
@@ -709,7 +709,7 @@ static void dk_compress_output(struct drc_kernel *dk)
#include <emmintrin.h>
static void dk_compress_output(struct drc_kernel *dk)
{
- const float master_linear_gain = dk->master_linear_gain;
+ const float main_linear_gain = dk->main_linear_gain;
const float envelope_rate = dk->envelope_rate;
const float scaled_desired_gain = dk->scaled_desired_gain;
const float compressor_gain = dk->compressor_gain;
@@ -789,7 +789,7 @@ static void dk_compress_output(struct drc_kernel *dk)
[A7]"x"(A7),
[base]"x"(_mm_set1_ps(scaled_desired_gain)),
[r4]"x"(_mm_set1_ps(r*r*r*r)),
- [g]"x"(_mm_set1_ps(master_linear_gain))
+ [g]"x"(_mm_set1_ps(main_linear_gain))
: /* clobber */
"memory", "cc");
// clang-format on
@@ -862,7 +862,7 @@ static void dk_compress_output(struct drc_kernel *dk)
[A7]"x"(A7),
[one]"x"(_mm_set1_ps(1)),
[r4]"x"(_mm_set1_ps(r*r*r*r)),
- [g]"x"(_mm_set1_ps(master_linear_gain))
+ [g]"x"(_mm_set1_ps(main_linear_gain))
: /* clobber */
"memory", "cc");
// clang-format on
@@ -872,7 +872,7 @@ static void dk_compress_output(struct drc_kernel *dk)
#else
static void dk_compress_output(struct drc_kernel *dk)
{
- const float master_linear_gain = dk->master_linear_gain;
+ const float main_linear_gain = dk->main_linear_gain;
const float envelope_rate = dk->envelope_rate;
const float scaled_desired_gain = dk->scaled_desired_gain;
const float compressor_gain = dk->compressor_gain;
@@ -902,8 +902,8 @@ static void dk_compress_output(struct drc_kernel *dk)
float post_warp_compressor_gain =
warp_sinf(x[j] + base);
- /* Calculate total gain using master gain. */
- float total_gain = master_linear_gain *
+ /* Calculate total gain using main gain. */
+ float total_gain = main_linear_gain *
post_warp_compressor_gain;
/* Apply final gain. */
@@ -936,8 +936,8 @@ static void dk_compress_output(struct drc_kernel *dk)
float post_warp_compressor_gain =
warp_sinf(x[j]);
- /* Calculate total gain using master gain. */
- float total_gain = master_linear_gain *
+ /* Calculate total gain using main gain. */
+ float total_gain = main_linear_gain *
post_warp_compressor_gain;
/* Apply final gain. */
diff --git a/cras/src/dsp/drc_kernel.h b/cras/src/dsp/drc_kernel.h
index 1157f225..2ed9956e 100644
--- a/cras/src/dsp/drc_kernel.h
+++ b/cras/src/dsp/drc_kernel.h
@@ -67,7 +67,7 @@ struct drc_kernel {
float kA, kB, kC, kD, kE;
/* Calculated parameters */
- float master_linear_gain;
+ float main_linear_gain;
float attack_frames;
float sat_release_frames_inv_neg;
float sat_release_rate_at_neg_two_db;
diff --git a/cras/src/libcras/cras_client.c b/cras/src/libcras/cras_client.c
index 3fe631d5..8420db1f 100644
--- a/cras/src/libcras/cras_client.c
+++ b/cras/src/libcras/cras_client.c
@@ -119,7 +119,8 @@ struct thread_state {
};
/* Parameters used when setting up a capture or playback stream. See comment
- * above cras_client_create_stream_params in the header for descriptions. */
+ * above cras_client_stream_params_create or libcras_stream_params_set in the
+ * header for descriptions. */
struct cras_stream_params {
enum CRAS_STREAM_DIRECTION direction;
size_t buffer_frames;
@@ -133,6 +134,7 @@ struct cras_stream_params {
cras_unified_cb_t unified_cb;
cras_error_cb_t err_cb;
struct cras_audio_format format;
+ libcras_stream_cb_t stream_cb;
};
/* Represents an attached audio stream.
@@ -274,6 +276,92 @@ struct cras_hotword_handle {
void *user_data;
};
+struct cras_stream_cb_data {
+ cras_stream_id_t stream_id;
+ enum CRAS_STREAM_DIRECTION direction;
+ uint8_t *buf;
+ unsigned int frames;
+ struct timespec sample_ts;
+ void *user_arg;
+};
+
+int stream_cb_get_stream_id(struct cras_stream_cb_data *data,
+ cras_stream_id_t *id)
+{
+ *id = data->stream_id;
+ return 0;
+}
+
+int stream_cb_get_buf(struct cras_stream_cb_data *data, uint8_t **buf)
+{
+ *buf = data->buf;
+ return 0;
+}
+
+int stream_cb_get_frames(struct cras_stream_cb_data *data, unsigned int *frames)
+{
+ *frames = data->frames;
+ return 0;
+}
+
+int stream_cb_get_latency(struct cras_stream_cb_data *data,
+ struct timespec *latency)
+{
+ if (data->direction == CRAS_STREAM_INPUT)
+ cras_client_calc_capture_latency(&data->sample_ts, latency);
+ else
+ cras_client_calc_playback_latency(&data->sample_ts, latency);
+ return 0;
+}
+
+int stream_cb_get_user_arg(struct cras_stream_cb_data *data, void **user_arg)
+{
+ *user_arg = data->user_arg;
+ return 0;
+}
+
+struct libcras_stream_cb_data *
+libcras_stream_cb_data_create(cras_stream_id_t stream_id,
+ enum CRAS_STREAM_DIRECTION direction,
+ uint8_t *buf, unsigned int frames,
+ struct timespec sample_ts, void *user_arg)
+{
+ struct libcras_stream_cb_data *data =
+ (struct libcras_stream_cb_data *)calloc(
+ 1, sizeof(struct libcras_stream_cb_data));
+ if (!data) {
+ syslog(LOG_ERR, "cras_client: calloc: %s", strerror(errno));
+ return NULL;
+ }
+ data->data_ = (struct cras_stream_cb_data *)calloc(
+ 1, sizeof(struct cras_stream_cb_data));
+ if (!data->data_) {
+ syslog(LOG_ERR, "cras_client: calloc: %s", strerror(errno));
+ free(data);
+ return NULL;
+ }
+ data->api_version = CRAS_API_VERSION;
+ data->get_stream_id = stream_cb_get_stream_id;
+ data->get_buf = stream_cb_get_buf;
+ data->get_frames = stream_cb_get_frames;
+ data->get_latency = stream_cb_get_latency;
+ data->get_user_arg = stream_cb_get_user_arg;
+ data->data_->stream_id = stream_id;
+ data->data_->direction = direction;
+ data->data_->buf = buf;
+ data->data_->frames = frames;
+ data->data_->sample_ts = sample_ts;
+ data->data_->user_arg = user_arg;
+ return data;
+}
+
+void libcras_stream_cb_data_destroy(struct libcras_stream_cb_data *data)
+{
+ if (data)
+ free(data->data_);
+ free(data);
+}
+
/*
* Local Helpers
*/
@@ -283,6 +371,10 @@ static int client_thread_rm_stream(struct cras_client *client,
static int handle_message_from_server(struct cras_client *client);
static int reregister_notifications(struct cras_client *client);
+static struct libcras_node_info *
+libcras_node_info_create(struct cras_iodev_info *iodev,
+ struct cras_ionode_info *ionode);
+
/*
* Unlock the server_state_rwlock if lock_rc is 0.
*
@@ -1084,6 +1176,7 @@ static int handle_capture_data_ready(struct client_stream *stream,
uint8_t *captured_frames;
struct timespec ts;
int rc = 0;
+ struct libcras_stream_cb_data *data;
config = stream->config;
/* If this message is for an output stream, log error and drop it. */
@@ -1098,14 +1191,24 @@ static int handle_capture_data_ready(struct client_stream *stream,
cras_timespec_to_timespec(&ts, &stream->shm->header->ts);
- if (config->unified_cb)
+ if (config->stream_cb) {
+ data = libcras_stream_cb_data_create(
+ stream->id, stream->direction, captured_frames,
+ num_frames, ts, config->user_data);
+ if (!data)
+ return -errno;
+ frames = config->stream_cb(data);
+ libcras_stream_cb_data_destroy(data);
+ data = NULL;
+ } else if (config->unified_cb) {
frames = config->unified_cb(stream->client, stream->id,
captured_frames, NULL, num_frames,
&ts, NULL, config->user_data);
- else
+ } else {
frames = config->aud_cb(stream->client, stream->id,
captured_frames, num_frames, &ts,
config->user_data);
+ }
if (frames < 0) {
send_stream_message(stream, CLIENT_STREAM_EOF);
rc = frames;
@@ -1152,6 +1255,7 @@ static int handle_playback_request(struct client_stream *stream,
struct cras_stream_params *config;
struct cras_audio_shm *shm = stream->shm;
struct timespec ts;
+ struct libcras_stream_cb_data *data;
config = stream->config;
@@ -1169,13 +1273,24 @@ static int handle_playback_request(struct client_stream *stream,
cras_timespec_to_timespec(&ts, &shm->header->ts);
/* Get samples from the user */
- if (config->unified_cb)
+ if (config->stream_cb) {
+ data = libcras_stream_cb_data_create(stream->id,
+ stream->direction, buf,
+ num_frames, ts,
+ config->user_data);
+ if (!data)
+ return -errno;
+ frames = config->stream_cb(data);
+ libcras_stream_cb_data_destroy(data);
+ data = NULL;
+ } else if (config->unified_cb) {
frames = config->unified_cb(stream->client, stream->id, NULL,
buf, num_frames, NULL, &ts,
config->user_data);
- else
+ } else {
frames = config->aud_cb(stream->client, stream->id, buf,
num_frames, &ts, config->user_data);
+ }
if (frames < 0) {
send_stream_message(stream, CLIENT_STREAM_EOF);
rc = frames;
@@ -2255,6 +2370,7 @@ struct cras_stream_params *cras_client_stream_params_create(
params->user_data = user_data;
params->aud_cb = aud_cb;
params->unified_cb = 0;
+ params->stream_cb = 0;
params->err_cb = err_cb;
memcpy(&(params->format), format, sizeof(*format));
return params;
@@ -2328,6 +2444,7 @@ struct cras_stream_params *cras_client_unified_params_create(
params->user_data = user_data;
params->aud_cb = 0;
params->unified_cb = unified_cb;
+ params->stream_cb = 0;
params->err_cb = err_cb;
memcpy(&(params->format), format, sizeof(*format));
@@ -2350,7 +2467,8 @@ static inline int cras_client_send_add_stream_command_message(
if (client == NULL || config == NULL || stream_id_out == NULL)
return -EINVAL;
- if (config->aud_cb == NULL && config->unified_cb == NULL)
+ if (config->stream_cb == NULL && config->aud_cb == NULL &&
+ config->unified_cb == NULL)
return -EINVAL;
if (config->err_cb == NULL)
@@ -3815,3 +3933,317 @@ int cras_client_disable_hotword_callback(struct cras_client *client,
free(handle);
return 0;
}
+
+int get_nodes(struct cras_client *client, enum CRAS_STREAM_DIRECTION direction,
+ struct libcras_node_info ***nodes, size_t *num)
+{
+ struct cras_iodev_info iodevs[CRAS_MAX_IODEVS];
+ struct cras_ionode_info ionodes[CRAS_MAX_IONODES];
+ size_t num_devs = CRAS_MAX_IODEVS, num_nodes = CRAS_MAX_IONODES;
+ int rc, i, j;
+
+ *num = 0;
+ if (direction == CRAS_STREAM_INPUT) {
+ rc = cras_client_get_input_devices(client, iodevs, ionodes,
+ &num_devs, &num_nodes);
+ } else {
+ rc = cras_client_get_output_devices(client, iodevs, ionodes,
+ &num_devs, &num_nodes);
+ }
+
+ if (rc < 0) {
+ syslog(LOG_ERR, "Failed to get devices: %d", rc);
+ return rc;
+ }
+
+ *nodes = (struct libcras_node_info **)calloc(
+ num_nodes, sizeof(struct libcras_node_info *));
+
+ for (i = 0; i < num_devs; i++) {
+ for (j = 0; j < num_nodes; j++) {
+ if (iodevs[i].idx != ionodes[j].iodev_idx)
+ continue;
+ (*nodes)[*num] = libcras_node_info_create(&iodevs[i],
+ &ionodes[j]);
+ if ((*nodes)[*num] == NULL) {
+ rc = -errno;
+ goto clean;
+ }
+ (*num)++;
+ }
+ }
+ return 0;
+clean:
+ for (i = 0; i < *num; i++)
+ libcras_node_info_destroy((*nodes)[i]);
+ free(*nodes);
+ *nodes = NULL;
+ *num = 0;
+ return rc;
+}
+
+int get_default_output_buffer_size(struct cras_client *client, int *size)
+{
+ int rc = cras_client_get_default_output_buffer_size(client);
+ if (rc < 0)
+ return rc;
+ *size = rc;
+ return 0;
+}
+
+int get_aec_group_id(struct cras_client *client, int *id)
+{
+ int rc = cras_client_get_aec_group_id(client);
+ if (rc < 0)
+ return rc;
+ *id = rc;
+ return 0;
+}
+
+int get_aec_supported(struct cras_client *client, int *supported)
+{
+ *supported = cras_client_get_aec_supported(client);
+ return 0;
+}
+
+int get_system_muted(struct cras_client *client, int *muted)
+{
+ *muted = cras_client_get_system_muted(client);
+ return 0;
+}
+
+int get_loopback_dev_idx(struct cras_client *client, int *idx)
+{
+ int rc = cras_client_get_first_dev_type_idx(
+ client, CRAS_NODE_TYPE_POST_MIX_PRE_DSP, CRAS_STREAM_INPUT);
+ if (rc < 0)
+ return rc;
+ *idx = rc;
+ return 0;
+}
+
+struct libcras_client *libcras_client_create()
+{
+ struct libcras_client *client = (struct libcras_client *)calloc(
+ 1, sizeof(struct libcras_client));
+ if (!client) {
+ syslog(LOG_ERR, "cras_client: calloc failed");
+ return NULL;
+ }
+ if (cras_client_create(&client->client_)) {
+ libcras_client_destroy(client);
+ return NULL;
+ }
+ client->api_version = CRAS_API_VERSION;
+ client->connect = cras_client_connect;
+ client->connect_timeout = cras_client_connect_timeout;
+ client->connected_wait = cras_client_connected_wait;
+ client->run_thread = cras_client_run_thread;
+ client->stop = cras_client_stop;
+ client->add_pinned_stream = cras_client_add_pinned_stream;
+ client->rm_stream = cras_client_rm_stream;
+ client->set_stream_volume = cras_client_set_stream_volume;
+ client->get_nodes = get_nodes;
+ client->get_default_output_buffer_size = get_default_output_buffer_size;
+ client->get_aec_group_id = get_aec_group_id;
+ client->get_aec_supported = get_aec_supported;
+ client->get_system_muted = get_system_muted;
+ client->set_system_mute = cras_client_set_system_mute;
+ client->get_loopback_dev_idx = get_loopback_dev_idx;
+ return client;
+}
+
+void libcras_client_destroy(struct libcras_client *client)
+{
+ cras_client_destroy(client->client_);
+ free(client);
+}
+
+int stream_params_set(struct cras_stream_params *params,
+ enum CRAS_STREAM_DIRECTION direction,
+ size_t buffer_frames, size_t cb_threshold,
+ enum CRAS_STREAM_TYPE stream_type,
+ enum CRAS_CLIENT_TYPE client_type, uint32_t flags,
+ void *user_data, libcras_stream_cb_t stream_cb,
+ cras_error_cb_t err_cb, size_t rate,
+ snd_pcm_format_t format, size_t num_channels)
+{
+ params->direction = direction;
+ params->buffer_frames = buffer_frames;
+ params->cb_threshold = cb_threshold;
+ params->stream_type = stream_type;
+ params->client_type = client_type;
+ params->flags = flags;
+ params->user_data = user_data;
+ params->stream_cb = stream_cb;
+ params->err_cb = err_cb;
+ params->format.frame_rate = rate;
+ params->format.format = format;
+ params->format.num_channels = num_channels;
+ return 0;
+}
+
+int stream_params_set_channel_layout(struct cras_stream_params *params,
+ int length, const int8_t *layout)
+{
+ if (length != CRAS_CH_MAX)
+ return -EINVAL;
+ return cras_audio_format_set_channel_layout(&params->format, layout);
+}
+
+struct libcras_stream_params *libcras_stream_params_create()
+{
+ struct libcras_stream_params *params =
+ (struct libcras_stream_params *)calloc(
+ 1, sizeof(struct libcras_stream_params));
+ if (!params) {
+ syslog(LOG_ERR, "cras_client: calloc failed");
+ return NULL;
+ }
+ params->params_ = (struct cras_stream_params *)calloc(
+ 1, sizeof(struct cras_stream_params));
+ if (params->params_ == NULL) {
+ syslog(LOG_ERR, "cras_client: calloc failed");
+ free(params->params_);
+ return NULL;
+ }
+ params->api_version = CRAS_API_VERSION;
+ params->set = stream_params_set;
+ params->set_channel_layout = stream_params_set_channel_layout;
+ params->enable_aec = cras_client_stream_params_enable_aec;
+ return params;
+}
+
+void libcras_stream_params_destroy(struct libcras_stream_params *params)
+{
+ free(params->params_);
+ free(params);
+}
+
+struct cras_node_info {
+ uint64_t id;
+ uint32_t dev_idx;
+ uint32_t node_idx;
+ uint32_t max_supported_channels;
+ bool plugged;
+ bool active;
+ char type[CRAS_NODE_TYPE_BUFFER_SIZE];
+ char node_name[CRAS_NODE_NAME_BUFFER_SIZE];
+ char dev_name[CRAS_IODEV_NAME_BUFFER_SIZE];
+};
+
+int cras_node_info_get_id(struct cras_node_info *node, uint64_t *id)
+{
+ (*id) = node->id;
+ return 0;
+}
+
+int cras_node_info_get_dev_idx(struct cras_node_info *node, uint32_t *dev_idx)
+{
+ (*dev_idx) = node->dev_idx;
+ return 0;
+}
+
+int cras_node_info_get_node_idx(struct cras_node_info *node, uint32_t *node_idx)
+{
+ (*node_idx) = node->node_idx;
+ return 0;
+}
+
+int cras_node_info_get_max_supported_channels(struct cras_node_info *node,
+ uint32_t *max_supported_channels)
+{
+ (*max_supported_channels) = node->max_supported_channels;
+ return 0;
+}
+
+int cras_node_info_is_plugged(struct cras_node_info *node, bool *is_plugged)
+{
+ (*is_plugged) = node->plugged;
+ return 0;
+}
+
+int cras_node_info_is_active(struct cras_node_info *node, bool *is_active)
+{
+ (*is_active) = node->active;
+ return 0;
+}
+
+int cras_node_info_get_type(struct cras_node_info *node, char **type)
+{
+ (*type) = node->type;
+ return 0;
+}
+
+int cras_node_info_get_node_name(struct cras_node_info *node, char **node_name)
+{
+ (*node_name) = node->node_name;
+ return 0;
+}
+
+int cras_node_info_get_dev_name(struct cras_node_info *node, char **dev_name)
+{
+ (*dev_name) = node->dev_name;
+ return 0;
+}
+
+struct libcras_node_info *
+libcras_node_info_create(struct cras_iodev_info *iodev,
+ struct cras_ionode_info *ionode)
+{
+ struct libcras_node_info *node = (struct libcras_node_info *)calloc(
+ 1, sizeof(struct libcras_node_info));
+ if (!node) {
+ syslog(LOG_ERR, "cras_client: calloc failed");
+ return NULL;
+ }
+ node->node_ = (struct cras_node_info *)calloc(
+ 1, sizeof(struct cras_node_info));
+ if (node->node_ == NULL) {
+ syslog(LOG_ERR, "cras_client: calloc failed");
+ free(node);
+ return NULL;
+ }
+ node->api_version = CRAS_API_VERSION;
+ node->node_->id =
+ cras_make_node_id(ionode->iodev_idx, ionode->ionode_idx);
+ node->node_->dev_idx = ionode->iodev_idx;
+ node->node_->node_idx = ionode->ionode_idx;
+ node->node_->max_supported_channels = iodev->max_supported_channels;
+ node->node_->plugged = ionode->plugged;
+ node->node_->active = ionode->active;
+ strncpy(node->node_->type, ionode->type, CRAS_NODE_TYPE_BUFFER_SIZE);
+ node->node_->type[CRAS_NODE_TYPE_BUFFER_SIZE - 1] = '\0';
+ strncpy(node->node_->node_name, ionode->name,
+ CRAS_NODE_NAME_BUFFER_SIZE);
+ node->node_->node_name[CRAS_NODE_NAME_BUFFER_SIZE - 1] = '\0';
+ strncpy(node->node_->dev_name, iodev->name,
+ CRAS_IODEV_NAME_BUFFER_SIZE);
+ node->node_->dev_name[CRAS_IODEV_NAME_BUFFER_SIZE - 1] = '\0';
+ node->get_id = cras_node_info_get_id;
+ node->get_dev_idx = cras_node_info_get_dev_idx;
+ node->get_node_idx = cras_node_info_get_node_idx;
+ node->get_max_supported_channels =
+ cras_node_info_get_max_supported_channels;
+ node->is_plugged = cras_node_info_is_plugged;
+ node->is_active = cras_node_info_is_active;
+ node->get_type = cras_node_info_get_type;
+ node->get_node_name = cras_node_info_get_node_name;
+ node->get_dev_name = cras_node_info_get_dev_name;
+ return node;
+}
+
+void libcras_node_info_destroy(struct libcras_node_info *node)
+{
+ free(node->node_);
+ free(node);
+}
+
+void libcras_node_info_array_destroy(struct libcras_node_info **nodes,
+ size_t num)
+{
+ int i;
+ for (i = 0; i < num; i++)
+ libcras_node_info_destroy(nodes[i]);
+ free(nodes);
+}
diff --git a/cras/src/libcras/cras_client.h b/cras/src/libcras/cras_client.h
index f7a18b5b..f26a0814 100644
--- a/cras/src/libcras/cras_client.h
+++ b/cras/src/libcras/cras_client.h
@@ -1308,6 +1308,699 @@ int cras_client_set_input_node_gain_changed_callback(
int cras_client_set_num_active_streams_changed_callback(
struct cras_client *client,
cras_client_num_active_streams_changed_callback cb);
+
+/*
+ * The functions below prefixed with libcras wrap the original CRAS library
+ * They provide an interface that maps the pointers to the functions above.
+ * Please add a new function instead of modifying the existing function.
+ * Here are some rules about how to add a new function:
+ * 1. Increase the CRAS_API_VERSION by 1.
+ * 2. Write a new function in cras_client.c.
+ * 3. Append the corresponding pointer to the structure. Remeber DO NOT change
+ * the order of functions in the structs.
+ * 4. Assign the pointer to the new function in cras_client.c.
+ * 5. Create the inline function in cras_client.h, which is used by clients.
+ * Remember to add DISABLE_CFI_ICALL on the inline function.
+ * 6. Add CHECK_VERSION in the inline function. If the api_version is smaller
+ * than the supported version, this inline function will return -ENOSYS.
+ */
+
+#define CRAS_API_VERSION 1
+#define CHECK_VERSION(object, version) \
+ if (object->api_version < version) { \
+ return -ENOSYS; \
+ }
+
+/*
+ * The inline functions use the indirect function call. Therefore, they are
+ * incompatible with CFI-icall.
+ */
+#define DISABLE_CFI_ICALL __attribute__((no_sanitize("cfi-icall")))
+
+struct libcras_node_info {
+ int api_version;
+ struct cras_node_info *node_;
+ int (*get_id)(struct cras_node_info *node, uint64_t *id);
+ int (*get_dev_idx)(struct cras_node_info *node, uint32_t *dev_idx);
+ int (*get_node_idx)(struct cras_node_info *node, uint32_t *node_idx);
+ int (*get_max_supported_channels)(struct cras_node_info *node,
+ uint32_t *max_supported_channels);
+ int (*is_plugged)(struct cras_node_info *node, bool *plugged);
+ int (*is_active)(struct cras_node_info *node, bool *active);
+ int (*get_type)(struct cras_node_info *node, char **name);
+ int (*get_node_name)(struct cras_node_info *node, char **name);
+ int (*get_dev_name)(struct cras_node_info *node, char **name);
+};
+
+struct libcras_client {
+ int api_version;
+ struct cras_client *client_;
+ int (*connect)(struct cras_client *client);
+ int (*connect_timeout)(struct cras_client *client,
+ unsigned int timeout_ms);
+ int (*connected_wait)(struct cras_client *client);
+ int (*run_thread)(struct cras_client *client);
+ int (*stop)(struct cras_client *client);
+ int (*add_pinned_stream)(struct cras_client *client, uint32_t dev_idx,
+ cras_stream_id_t *stream_id_out,
+ struct cras_stream_params *config);
+ int (*rm_stream)(struct cras_client *client,
+ cras_stream_id_t stream_id);
+ int (*set_stream_volume)(struct cras_client *client,
+ cras_stream_id_t stream_id,
+ float volume_scaler);
+ int (*get_nodes)(struct cras_client *client,
+ enum CRAS_STREAM_DIRECTION direction,
+ struct libcras_node_info ***nodes, size_t *num);
+ int (*get_default_output_buffer_size)(struct cras_client *client,
+ int *size);
+ int (*get_aec_group_id)(struct cras_client *client, int *id);
+ int (*get_aec_supported)(struct cras_client *client, int *supported);
+ int (*get_system_muted)(struct cras_client *client, int *muted);
+ int (*set_system_mute)(struct cras_client *client, int mute);
+ int (*get_loopback_dev_idx)(struct cras_client *client, int *idx);
+};
+
+struct cras_stream_cb_data;
+struct libcras_stream_cb_data {
+ int api_version;
+ struct cras_stream_cb_data *data_;
+ int (*get_stream_id)(struct cras_stream_cb_data *data,
+ cras_stream_id_t *id);
+ int (*get_buf)(struct cras_stream_cb_data *data, uint8_t **buf);
+ int (*get_frames)(struct cras_stream_cb_data *data,
+ unsigned int *frames);
+ int (*get_latency)(struct cras_stream_cb_data *data,
+ struct timespec *latency);
+ int (*get_user_arg)(struct cras_stream_cb_data *data, void **user_arg);
+};
+typedef int (*libcras_stream_cb_t)(struct libcras_stream_cb_data *data);
+
+struct libcras_stream_params {
+ int api_version;
+ struct cras_stream_params *params_;
+ int (*set)(struct cras_stream_params *params,
+ enum CRAS_STREAM_DIRECTION direction, size_t buffer_frames,
+ size_t cb_threshold, enum CRAS_STREAM_TYPE stream_type,
+ enum CRAS_CLIENT_TYPE client_type, uint32_t flags,
+ void *user_data, libcras_stream_cb_t stream_cb,
+ cras_error_cb_t err_cb, size_t rate, snd_pcm_format_t format,
+ size_t num_channels);
+ int (*set_channel_layout)(struct cras_stream_params *params, int length,
+ const int8_t *layout);
+ void (*enable_aec)(struct cras_stream_params *params);
+};
+
+/*
+ * Creates a new client.
+ * Returns:
+ * If success, return a valid libcras_client pointer. Otherwise, return
+ * NULL.
+ */
+struct libcras_client *libcras_client_create();
+
+/*
+ * Destroys a client.
+ * Args:
+ * client - pointer returned from "libcras_client_create".
+ */
+void libcras_client_destroy(struct libcras_client *client);
+
+/*
+ * Connects a client to the running server.
+ * Waits forever (until interrupted or connected).
+ * Args:
+ * client - pointer returned from "libcras_client_create".
+ * Returns:
+ * 0 on success, or a negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_client_connect(struct libcras_client *client)
+{
+ return client->connect(client->client_);
+}
+
+/*
+ * Connects a client to the running server, retries until timeout.
+ * Args:
+ * client - pointer returned from "libcras_client_create".
+ * timeout_ms - timeout in milliseconds or negative to wait forever.
+ * Returns:
+ * 0 on success, or a negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_client_connect_timeout(struct libcras_client *client,
+ unsigned int timeout_ms)
+{
+ return client->connect_timeout(client->client_, timeout_ms);
+}
+
+/*
+ * Wait up to 1 second for the client thread to complete the server connection.
+ *
+ * After libcras_client_run_thread() is executed, this function can be
+ * used to ensure that the connection has been established with the server and
+ * ensure that any information about the server is up to date. If
+ * libcras_client_run_thread() has not yet been executed, or
+ * libcras_client_stop() was executed and thread isn't running, then this
+ * function returns -EINVAL.
+ *
+ * Args:
+ * client - pointer returned from "libcras_client_create".
+ * Returns:
+ * 0 on success, or a negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_client_connected_wait(struct libcras_client *client)
+{
+ return client->connected_wait(client->client_);
+}
+
+/*
+ * Begins running the client control thread.
+ *
+ * Required for stream operations and other operations noted below.
+ *
+ * Args:
+ * client - pointer returned from "libcras_client_create".
+ * Returns:
+ * 0 on success, or a negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_client_run_thread(struct libcras_client *client)
+{
+ return client->run_thread(client->client_);
+}
+
+/*
+ * Stops running a client.
+ * This function is executed automatically by cras_client_destroy().
+ * Args:
+ * client - pointer returned from "libcras_client_create".
+ * Returns:
+ * 0 on success or if the thread was already stopped, -EINVAL if the client
+ * isn't valid.
+ */
+DISABLE_CFI_ICALL
+inline int libcras_client_stop(struct libcras_client *client)
+{
+ return client->stop(client->client_);
+}
+
+/*
+ * Creates a pinned stream and return the stream id or < 0 on error.
+ *
+ * Requires execution of libcras_client_run_thread(), and an active
+ * connection to the audio server.
+ *
+ * Args:
+ * client - pointer returned from "libcras_client_create".
+ * dev_idx - Index of the device to attach the newly created stream.
+ * stream_id_out - On success will be filled with the new stream id.
+ * Guaranteed to be set before any callbacks are made.
+ * params - The pointer specifying the parameters for the stream.
+ * (returned from libcras_stream_params_create)
+ * Returns:
+ * 0 on success, negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_client_add_pinned_stream(
+ struct libcras_client *client, uint32_t dev_idx,
+ cras_stream_id_t *stream_id_out, struct libcras_stream_params *params)
+{
+ return client->add_pinned_stream(client->client_, dev_idx,
+ stream_id_out, params->params_);
+}
+
+/*
+ * Removes a currently playing/capturing stream.
+ *
+ * Requires execution of libcras_client_run_thread().
+ *
+ * Args:
+ * client - pointer returned from "libcras_client_create".
+ * stream_id - ID returned from libcras_client_add_stream to identify
+ * the stream to remove.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_client_rm_stream(struct libcras_client *client,
+ cras_stream_id_t stream_id)
+{
+ return client->rm_stream(client->client_, stream_id);
+}
+
+/*
+ * Sets the volume scaling factor for the given stream.
+ *
+ * Requires execution of cras_client_run_thread().
+ *
+ * Args:
+ * client - pointer returned from "libcras_client_create".
+ * stream_id - ID returned from libcras_client_add_stream.
+ * volume_scaler - 0.0-1.0 the new value to scale this stream by.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_client_set_stream_volume(struct libcras_client *client,
+ cras_stream_id_t stream_id,
+ float volume_scaler)
+{
+ return client->set_stream_volume(client->client_, stream_id,
+ volume_scaler);
+}
+
+/*
+ * Gets the current list of audio nodes.
+ *
+ * Args:
+ * client - Pointer returned from "libcras_client_create".
+ * direction - Input or output.
+ * nodes - Array that will be filled with libcras_node_info pointers.
+ * num - Pointer to store the size of the array.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ * Remember to call libcras_node_info_array_destroy to free the array.
+ */
+DISABLE_CFI_ICALL
+inline int libcras_client_get_nodes(struct libcras_client *client,
+ enum CRAS_STREAM_DIRECTION direction,
+ struct libcras_node_info ***nodes,
+ size_t *num)
+{
+ return client->get_nodes(client->client_, direction, nodes, num);
+}
+
+/*
+ * Gets the default output buffer size.
+ * Args:
+ * client - Pointer returned from "libcras_client_create".
+ * size - The pointer to save the result.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int
+libcras_client_get_default_output_buffer_size(struct libcras_client *client,
+ int *size)
+{
+ return client->get_default_output_buffer_size(client->client_, size);
+}
+
+/*
+ * Gets the AEC group ID.
+ * Args:
+ * client - Pointer returned from "libcras_client_create".
+ * id - The pointer to save the result.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_client_get_aec_group_id(struct libcras_client *client,
+ int *id)
+{
+ return client->get_aec_group_id(client->client_, id);
+}
+
+/*
+ * Gets whether AEC is supported.
+ * Args:
+ * client - Pointer returned from "libcras_client_create".
+ * supported - The pointer to save the result.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_client_get_aec_supported(struct libcras_client *client,
+ int *supported)
+{
+ return client->get_aec_supported(client->client_, supported);
+}
+
+/*
+ * Gets whether the system is muted.
+ * Args:
+ * client - Pointer returned from "libcras_client_create".
+ * muted - The pointer to save the result.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_client_get_system_muted(struct libcras_client *client,
+ int *muted)
+{
+ return client->get_aec_group_id(client->client_, muted);
+}
+
+/*
+ * Mutes or unmutes the system.
+ * Args:
+ * client - Pointer returned from "libcras_client_create".
+ * mute - 1 is to mute and 0 is to unmute.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_client_set_system_mute(struct libcras_client *client,
+ int mute)
+{
+ return client->set_system_mute(client->client_, mute);
+}
+
+/*
+ * Gets the index of the loopback device.
+ * Args:
+ * client - Pointer returned from "libcras_client_create".
+ * idx - The pointer to save the result.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_client_get_loopback_dev_idx(struct libcras_client *client,
+ int *idx)
+{
+ return client->get_loopback_dev_idx(client->client_, idx);
+}
+
+/*
+ * Creates a new struct to save stream params.
+ * Returns:
+ * If success, return a valid libcras_stream_params pointer. Otherwise,
+ * return NULL.
+ */
+struct libcras_stream_params *libcras_stream_params_create();
+
+/*
+ * Destroys a stream params instance.
+ * Args:
+ * params - The pointer returned from libcras_stream_params_create.
+ */
+void libcras_stream_params_destroy(struct libcras_stream_params *params);
+
+/*
+ * Setup stream configuration parameters.
+ * Args:
+ * params - The pointer returned from libcras_stream_params_create.
+ * direction - Playback(CRAS_STREAM_OUTPUT) or capture(CRAS_STREAM_INPUT).
+ * buffer_frames - total number of audio frames to buffer (dictates latency).
+ * cb_threshold - For playback, call back for more data when the buffer
+ * reaches this level. For capture, this is ignored (Audio callback will
+ * be called when buffer_frames have been captured).
+ * stream_type - Media or talk (currently only support "default").
+ * client_type - The client type, like Chrome or CrOSVM.
+ * flags - Currently only used for CRAS_INPUT_STREAM_FLAG.
+ * user_data - Pointer that will be passed to the callback.
+ * stream_cb - The audio callback. Called when audio is needed(playback) or
+ * ready(capture).
+ * err_cb - Called when there is an error with the stream.
+ * rate - The sample rate of the audio stream.
+ * format - The format of the audio stream.
+ * num_channels - The number of channels of the audio stream.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_stream_params_set(
+ struct libcras_stream_params *params,
+ enum CRAS_STREAM_DIRECTION direction, size_t buffer_frames,
+ size_t cb_threshold, enum CRAS_STREAM_TYPE stream_type,
+ enum CRAS_CLIENT_TYPE client_type, uint32_t flags, void *user_data,
+ libcras_stream_cb_t stream_cb, cras_error_cb_t err_cb, size_t rate,
+ snd_pcm_format_t format, size_t num_channels)
+{
+ return params->set(params->params_, direction, buffer_frames,
+ cb_threshold, stream_type, client_type, flags,
+ user_data, stream_cb, err_cb, rate, format,
+ num_channels);
+}
+
+/*
+ * Sets channel layout on given stream parameter.
+ * Args:
+ * params - The pointer returned from libcras_stream_params_create.
+ * length - The length of the array.
+ * layout - An integer array representing the position of each channel in
+ * enum CRAS_CHANNEL.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int
+libcras_stream_params_set_channel_layout(struct libcras_stream_params *params,
+ int length, const int8_t *layout)
+{
+ return params->set_channel_layout(params->params_, length, layout);
+}
+
+/*
+ * Enables AEC on given stream parameter.
+ * Args:
+ * params - The pointer returned from libcras_stream_params_create.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int
+libcras_stream_params_enable_aec(struct libcras_stream_params *params)
+{
+ params->enable_aec(params->params_);
+ return 0;
+}
+
+/*
+ * Gets stream id from the callback data.
+ * Args:
+ * data - The pointer passed to the callback function.
+ * id - The pointer to save the stream id.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int
+libcras_stream_cb_data_get_stream_id(struct libcras_stream_cb_data *data,
+ cras_stream_id_t *id)
+{
+ return data->get_stream_id(data->data_, id);
+}
+
+/*
+ * Gets stream buf from the callback data.
+ * Args:
+ * data - The pointer passed to the callback function.
+ * buf - The pointer to save the stream buffer.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_stream_cb_data_get_buf(struct libcras_stream_cb_data *data,
+ uint8_t **buf)
+{
+ return data->get_buf(data->data_, buf);
+}
+
+/*
+ * Gets how many frames to read or play from the callback data.
+ * Args:
+ * data - The pointer passed to the callback function.
+ * frames - The pointer to save the number of frames.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int
+libcras_stream_cb_data_get_frames(struct libcras_stream_cb_data *data,
+ unsigned int *frames)
+{
+ return data->get_frames(data->data_, frames);
+}
+
+/*
+ * Gets the latency from the callback data.
+ * Args:
+ * data - The pointer passed to the callback function.
+ * frames - The timespec pointer to save the latency.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int
+libcras_stream_cb_data_get_latency(struct libcras_stream_cb_data *data,
+ struct timespec *latency)
+{
+ return data->get_latency(data->data_, latency);
+}
+
+/*
+ * Gets the user data from the callback data.
+ * Args:
+ * data - The pointer passed to the callback function.
+ * frames - The pointer to save the user data.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int
+libcras_stream_cb_data_get_usr_arg(struct libcras_stream_cb_data *data,
+ void **user_arg)
+{
+ return data->get_user_arg(data->data_, user_arg);
+}
+
+/*
+ * Destroys a node info instance.
+ * Args:
+ * node - The libcras_node_info pointer to destroy.
+ */
+void libcras_node_info_destroy(struct libcras_node_info *node);
+
+/*
+ * Destroys a node info array.
+ * Args:
+ * nodes - The libcras_node_info pointer array to destroy.
+ * num - The size of the array.
+ */
+void libcras_node_info_array_destroy(struct libcras_node_info **nodes,
+ size_t num);
+
+/*
+ * Gets ID from the node info pointer.
+ * Args:
+ * node - The node info pointer. (Returned from libcras_client_get_nodes)
+ * id - The pointer to save ID.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_node_info_get_id(struct libcras_node_info *node,
+ uint64_t *id)
+{
+ return node->get_id(node->node_, id);
+}
+
+/*
+ * Gets device index from the node info pointer.
+ * Args:
+ * node - The node info pointer. (Returned from libcras_client_get_nodes)
+ * dev_idx - The pointer to the device index.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_node_info_get_dev_idx(struct libcras_node_info *node,
+ uint32_t *dev_idx)
+{
+ return node->get_dev_idx(node->node_, dev_idx);
+}
+
+/*
+ * Gets node index from the node info pointer.
+ * Args:
+ * node - The node info pointer. (Returned from libcras_client_get_nodes)
+ * node_idx - The pointer to save the node index.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_node_info_get_node_idx(struct libcras_node_info *node,
+ uint32_t *node_idx)
+{
+ return node->get_node_idx(node->node_, node_idx);
+}
+
+/*
+ * Gets the max supported channels from the node info pointer.
+ * Args:
+ * node - The node info pointer. (Returned from libcras_client_get_nodes)
+ * max_supported_channels - The pointer to save the result.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int
+libcras_node_info_get_max_supported_channels(struct libcras_node_info *node,
+ uint32_t *max_supported_channels)
+{
+ return node->get_max_supported_channels(node->node_,
+ max_supported_channels);
+}
+
+/*
+ * Gets whether the node is plugged from the node info pointer.
+ * Args:
+ * node - The node info pointer. (Returned from libcras_client_get_nodes)
+ * plugged - The pointer to save the result.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_node_info_is_plugged(struct libcras_node_info *node,
+ bool *plugged)
+{
+ return node->is_plugged(node->node_, plugged);
+}
+
+/*
+ * Gets whether the node is active from the node info pointer.
+ * Args:
+ * node - The node info pointer. (Returned from libcras_client_get_nodes)
+ * active - The pointer to save the result.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_node_info_is_active(struct libcras_node_info *node,
+ bool *active)
+{
+ return node->is_active(node->node_, active);
+}
+
+/*
+ * Gets device type from the node info pointer.
+ * Args:
+ * node - The node info pointer. (Returned from libcras_client_get_nodes)
+ * type - The pointer to save the device type.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_node_info_get_type(struct libcras_node_info *node,
+ char **type)
+{
+ return node->get_type(node->node_, type);
+}
+
+/*
+ * Gets device name from the node info pointer.
+ * Args:
+ * node - The node info pointer. (Returned from libcras_client_get_nodes)
+ * name - The pointer to save the device name.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_node_info_get_node_name(struct libcras_node_info *node,
+ char **name)
+{
+ return node->get_node_name(node->node_, name);
+}
+
+/*
+ * Gets node name from the node info pointer.
+ * Args:
+ * node - The node info pointer. (Returned from libcras_client_get_nodes)
+ * name - The pointer to save the node name.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+DISABLE_CFI_ICALL
+inline int libcras_node_info_get_dev_name(struct libcras_node_info *node,
+ char **name)
+{
+ return node->get_dev_name(node->node_, name);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/cras/src/server/audio_thread.c b/cras/src/server/audio_thread.c
index cd155e82..48bb0dc2 100644
--- a/cras/src/server/audio_thread.c
+++ b/cras/src/server/audio_thread.c
@@ -443,7 +443,8 @@ static int thread_add_stream(struct audio_thread *thread,
{
int rc;
- rc = dev_io_append_stream(&thread->open_devs[stream->direction], stream,
+ rc = dev_io_append_stream(&thread->open_devs[CRAS_STREAM_OUTPUT],
+ &thread->open_devs[CRAS_STREAM_INPUT], stream,
iodevs, num_iodevs);
if (rc < 0)
return rc;
diff --git a/cras/src/server/config/cras_board_config.c b/cras/src/server/config/cras_board_config.c
index 14d3fa0c..e36ea3cf 100644
--- a/cras/src/server/config/cras_board_config.c
+++ b/cras/src/server/config/cras_board_config.c
@@ -14,6 +14,7 @@ static const int32_t AEC_SUPPORTED_DEFAULT = 0;
static const int32_t AEC_GROUP_ID_DEFAULT = -1;
static const int32_t BLUETOOTH_WBS_ENABLED_INI_DEFAULT = 1;
static const int32_t BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_DEFAULT = 0;
+static const int32_t HOTWORD_PAUSE_AT_SUSPEND_DEFAULT = 0;
#define CONFIG_NAME "board.ini"
#define DEFAULT_OUTPUT_BUF_SIZE_INI_KEY "output:default_output_buffer_size"
@@ -22,6 +23,7 @@ static const int32_t BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_DEFAULT = 0;
#define BLUETOOTH_WBS_ENABLED_INI_KEY "bluetooth:wbs_enabled"
#define BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_KEY "bluetooth:deprioritize_wbs_mic"
#define UCM_IGNORE_SUFFIX_KEY "ucm:ignore_suffix"
+#define HOTWORD_PAUSE_AT_SUSPEND "hotword:pause_at_suspend"
void cras_board_config_get(const char *config_path,
struct cras_board_config *board_config)
@@ -85,6 +87,11 @@ void cras_board_config_get(const char *config_path,
syslog(LOG_ERR, "Failed to call strdup: %d", errno);
}
+ snprintf(ini_key, MAX_INI_KEY_LENGTH, HOTWORD_PAUSE_AT_SUSPEND);
+ ini_key[MAX_INI_KEY_LENGTH] = 0;
+ board_config->hotword_pause_at_suspend = iniparser_getint(
+ ini, ini_key, HOTWORD_PAUSE_AT_SUSPEND_DEFAULT);
+
iniparser_freedict(ini);
syslog(LOG_DEBUG, "Loaded ini file %s", ini_name);
}
diff --git a/cras/src/server/config/cras_board_config.h b/cras/src/server/config/cras_board_config.h
index 2ecde265..d4bd8496 100644
--- a/cras/src/server/config/cras_board_config.h
+++ b/cras/src/server/config/cras_board_config.h
@@ -15,6 +15,7 @@ struct cras_board_config {
int32_t bt_wbs_enabled;
int32_t deprioritize_bt_wbs_mic;
char *ucm_ignore_suffix;
+ int32_t hotword_pause_at_suspend;
};
/* Gets a configuration based on the config file specified.
diff --git a/cras/src/server/cras_a2dp_iodev.c b/cras/src/server/cras_a2dp_iodev.c
index 6c434758..b8a606e4 100644
--- a/cras/src/server/cras_a2dp_iodev.c
+++ b/cras/src/server/cras_a2dp_iodev.c
@@ -24,7 +24,6 @@
#include "cras_bt_device.h"
#include "cras_iodev.h"
#include "cras_util.h"
-#include "sfh.h"
#include "rtp.h"
#include "utlist.h"
@@ -644,10 +643,7 @@ struct cras_iodev *a2dp_iodev_create(struct cras_bt_transport *transport)
snprintf(iodev->info.name, sizeof(iodev->info.name), "%s", name);
iodev->info.name[ARRAY_SIZE(iodev->info.name) - 1] = '\0';
- iodev->info.stable_id =
- SuperFastHash(cras_bt_device_object_path(device),
- strlen(cras_bt_device_object_path(device)),
- strlen(cras_bt_device_object_path(device)));
+ iodev->info.stable_id = cras_bt_device_get_stable_id(device);
iodev->configure_dev = configure_dev;
iodev->frames_queued = frames_queued;
diff --git a/cras/src/server/cras_alsa_io.c b/cras/src/server/cras_alsa_io.c
index da4ef630..275a6810 100644
--- a/cras/src/server/cras_alsa_io.c
+++ b/cras/src/server/cras_alsa_io.c
@@ -391,6 +391,7 @@ static int open_dev(struct cras_iodev *iodev)
snd_pcm_t *handle;
int rc;
const char *pcm_name = NULL;
+ int enable_noise_cancellation;
if (aio->base.direction == CRAS_STREAM_OUTPUT) {
struct alsa_output_node *aout =
@@ -412,6 +413,19 @@ static int open_dev(struct cras_iodev *iodev)
aio->handle = handle;
+ /* Enable or disable noise cancellation if it supports. */
+ if (aio->ucm && iodev->direction == CRAS_STREAM_INPUT &&
+ ucm_node_noise_cancellation_exists(aio->ucm,
+ iodev->active_node->name)) {
+ enable_noise_cancellation =
+ cras_system_get_noise_cancellation_enabled();
+ rc = ucm_enable_node_noise_cancellation(
+ aio->ucm, iodev->active_node->name,
+ enable_noise_cancellation);
+ if (rc < 0)
+ return rc;
+ }
+
return 0;
}
@@ -2021,6 +2035,17 @@ static int get_valid_frames(struct cras_iodev *odev, struct timespec *tstamp)
return 0;
}
+static int support_noise_cancellation(const struct cras_iodev *iodev)
+{
+ struct alsa_io *aio = (struct alsa_io *)iodev;
+
+ if (!aio->ucm || !iodev->active_node)
+ return 0;
+
+ return ucm_node_noise_cancellation_exists(aio->ucm,
+ iodev->active_node->name);
+}
+
/*
* Exported Interface.
*/
@@ -2098,6 +2123,7 @@ alsa_iodev_create(size_t card_index, const char *card_name, size_t device_index,
iodev->get_num_severe_underruns = get_num_severe_underruns;
iodev->get_valid_frames = get_valid_frames;
iodev->set_swap_mode_for_node = cras_iodev_dsp_set_swap_mode_for_node;
+ iodev->support_noise_cancellation = support_noise_cancellation;
if (card_type == ALSA_CARD_TYPE_USB)
iodev->min_buffer_level = USB_EXTRA_BUFFER_FRAMES;
@@ -2418,12 +2444,10 @@ static int alsa_iodev_set_active_node(struct cras_iodev *iodev,
unsigned dev_enabled)
{
struct alsa_io *aio = (struct alsa_io *)iodev;
+ int rc = 0;
- if (iodev->active_node == ionode) {
- enable_active_ucm(aio, dev_enabled);
- init_device_settings(aio);
- return 0;
- }
+ if (iodev->active_node == ionode)
+ goto skip;
/* Disable jack ucm before switching node. */
enable_active_ucm(aio, 0);
@@ -2433,7 +2457,16 @@ static int alsa_iodev_set_active_node(struct cras_iodev *iodev,
cras_iodev_set_active_node(iodev, ionode);
aio->base.dsp_name = get_active_dsp_name(aio);
cras_iodev_update_dsp(iodev);
+skip:
enable_active_ucm(aio, dev_enabled);
+ if (ionode->type == CRAS_NODE_TYPE_HOTWORD) {
+ if (dev_enabled) {
+ rc = ucm_enable_hotword_model(aio->ucm);
+ if (rc < 0)
+ return rc;
+ } else
+ ucm_disable_all_hotword_models(aio->ucm);
+ }
/* Setting the volume will also unmute if the system isn't muted. */
init_device_settings(aio);
return 0;
diff --git a/cras/src/server/cras_alsa_mixer.c b/cras/src/server/cras_alsa_mixer.c
index 10705573..3379d959 100644
--- a/cras/src/server/cras_alsa_mixer.c
+++ b/cras/src/server/cras_alsa_mixer.c
@@ -943,7 +943,7 @@ void cras_alsa_mixer_set_dBFS(struct cras_alsa_mixer *cras_mixer, long dBFS,
assert(cras_mixer);
/* dBFS is normally < 0 to specify the attenuation from max. max is the
- * combined max of the master controls and the current output.
+ * combined max of the main controls and the current output.
*/
to_set = dBFS + cras_mixer->max_volume_dB;
if (cras_alsa_mixer_has_volume(mixer_output))
diff --git a/cras/src/server/cras_alsa_mixer.h b/cras/src/server/cras_alsa_mixer.h
index 3f730cf5..878fbe54 100644
--- a/cras/src/server/cras_alsa_mixer.h
+++ b/cras/src/server/cras_alsa_mixer.h
@@ -147,7 +147,7 @@ void cras_alsa_mixer_set_mute(struct cras_alsa_mixer *cras_mixer, int muted,
* Args:
* cras_mixer - Mixer to set the volume in.
* muted - 1 if muted, 0 if not.
- * mixer_input - The mixer input to mute if no master mute.
+ * mixer_input - The mixer input to mute if no card mute.
*/
void cras_alsa_mixer_set_capture_mute(struct cras_alsa_mixer *cras_mixer,
int muted,
diff --git a/cras/src/server/cras_alsa_ucm.c b/cras/src/server/cras_alsa_ucm.c
index 9759a50f..3e46f6a9 100644
--- a/cras/src/server/cras_alsa_ucm.c
+++ b/cras/src/server/cras_alsa_ucm.c
@@ -22,7 +22,6 @@ static const char override_type_name_var[] = "OverrideNodeType";
static const char dsp_name_var[] = "DspName";
static const char playback_mixer_elem_var[] = "PlaybackMixerElem";
static const char capture_mixer_elem_var[] = "CaptureMixerElem";
-static const char swap_mode_suffix[] = "Swap Mode";
static const char min_buffer_level_var[] = "MinBufferLevel";
static const char dma_period_var[] = "DmaPeriodMicrosecs";
static const char disable_software_volume[] = "DisableSoftwareVolume";
@@ -38,6 +37,11 @@ static const char dependent_device_name_var[] = "DependentPCM";
static const char preempt_hotword_var[] = "PreemptHotword";
static const char echo_reference_dev_name_var[] = "EchoReferenceDev";
+/* SectionModifier prefixes and suffixes. */
+static const char hotword_model_prefix[] = "Hotword Model";
+static const char swap_mode_suffix[] = "Swap Mode";
+static const char noise_cancellation_suffix[] = "Noise Cancellation";
+
/*
* Set this value in a SectionDevice to specify the intrinsic sensitivity in
* 0.01 dBFS/Pa. It currently only supports input devices. You should get the
@@ -54,7 +58,6 @@ static const char intrinsic_sensitivity_var[] = "IntrinsicSensitivity";
* 0.01 dB.
*/
static const char default_node_gain[] = "DefaultNodeGain";
-static const char hotword_model_prefix[] = "Hotword Model";
static const char fully_specified_ucm_var[] = "FullySpecifiedUCM";
static const char main_volume_names[] = "MainVolumeNames";
@@ -64,6 +67,8 @@ static const char *use_case_verbs[] = {
"Speech", "Pro Audio", "Accessibility",
};
+static const size_t max_section_name_len = 100;
+
/* Represents a list of section names found in UCM. */
struct section_name {
const char *name;
@@ -72,9 +77,10 @@ struct section_name {
struct cras_use_case_mgr {
snd_use_case_mgr_t *mgr;
- const char *name;
+ char *name;
unsigned int avail_use_cases;
enum CRAS_STREAM_TYPE use_case;
+ char *hotword_modifier;
};
static inline const char *uc_verb(struct cras_use_case_mgr *mgr)
@@ -376,6 +382,21 @@ static struct mixer_name *ucm_get_mixer_names(struct cras_use_case_mgr *mgr,
return names;
}
+/* Gets the modifier name of Noise Cancellation for the given node_name. */
+static void ucm_get_node_noise_cancellation_name(const char *node_name,
+ char *mod_name)
+{
+ size_t len =
+ strlen(node_name) + 1 + strlen(noise_cancellation_suffix) + 1;
+ if (len > max_section_name_len) {
+ syslog(LOG_ERR,
+ "Length of the given section name is %zu > %zu(max)",
+ len, max_section_name_len);
+ len = max_section_name_len;
+ }
+ snprintf(mod_name, len, "%s %s", node_name, noise_cancellation_suffix);
+}
+
/* Exported Interface */
struct cras_use_case_mgr *ucm_create(const char *name)
@@ -394,6 +415,10 @@ struct cras_use_case_mgr *ucm_create(const char *name)
if (!mgr)
return NULL;
+ mgr->name = strdup(name);
+ if (!mgr->name)
+ goto cleanup;
+
rc = snd_use_case_mgr_open(&mgr->mgr, name);
if (rc) {
syslog(LOG_WARNING, "Can not open ucm for card %s, rc = %d",
@@ -401,8 +426,8 @@ struct cras_use_case_mgr *ucm_create(const char *name)
goto cleanup;
}
- mgr->name = name;
mgr->avail_use_cases = 0;
+ mgr->hotword_modifier = NULL;
num_verbs = snd_use_case_get_list(mgr->mgr, "_verbs", &list);
for (i = 0; i < num_verbs; i += 2) {
for (j = 0; j < CRAS_STREAM_NUM_TYPES; ++j) {
@@ -424,6 +449,7 @@ struct cras_use_case_mgr *ucm_create(const char *name)
cleanup_mgr:
snd_use_case_mgr_close(mgr->mgr);
cleanup:
+ free(mgr->name);
free(mgr);
return NULL;
}
@@ -431,6 +457,8 @@ cleanup:
void ucm_destroy(struct cras_use_case_mgr *mgr)
{
snd_use_case_mgr_close(mgr->mgr);
+ free(mgr->hotword_modifier);
+ free(mgr->name);
free(mgr);
}
@@ -487,6 +515,51 @@ int ucm_enable_swap_mode(struct cras_use_case_mgr *mgr, const char *node_name,
return rc;
}
+int ucm_node_noise_cancellation_exists(struct cras_use_case_mgr *mgr,
+ const char *node_name)
+{
+ char *node_modifier_name = NULL;
+ int exists;
+
+ node_modifier_name = (char *)malloc(max_section_name_len);
+ if (!node_modifier_name)
+ return 0;
+ ucm_get_node_noise_cancellation_name(node_name, node_modifier_name);
+ exists = ucm_mod_exists_with_name(mgr, node_modifier_name);
+ free((void *)node_modifier_name);
+ return exists;
+}
+
+int ucm_enable_node_noise_cancellation(struct cras_use_case_mgr *mgr,
+ const char *node_name, int enable)
+{
+ char *node_modifier_name = NULL;
+ int rc;
+
+ node_modifier_name = (char *)malloc(max_section_name_len);
+ if (!node_modifier_name)
+ return -ENOMEM;
+ ucm_get_node_noise_cancellation_name(node_name, node_modifier_name);
+ if (!ucm_mod_exists_with_name(mgr, node_modifier_name)) {
+ syslog(LOG_ERR, "Can not find modifier %s.",
+ node_modifier_name);
+ free((void *)node_modifier_name);
+ return -EPERM;
+ }
+ if (modifier_enabled(mgr, node_modifier_name) == !!enable) {
+ syslog(LOG_DEBUG, "Modifier %s is already %s.",
+ node_modifier_name, enable ? "enabled" : "disabled");
+ free((void *)node_modifier_name);
+ return 0;
+ }
+
+ syslog(LOG_DEBUG, "UCM %s Modifier %s", enable ? "enable" : "disable",
+ node_modifier_name);
+ rc = ucm_set_modifier_enabled(mgr, node_modifier_name, enable);
+ free((void *)node_modifier_name);
+ return rc;
+}
+
int ucm_set_enabled(struct cras_use_case_mgr *mgr, const char *dev, int enable)
{
int rc;
@@ -984,14 +1057,61 @@ char *ucm_get_hotword_models(struct cras_use_case_mgr *mgr)
return models;
}
-int ucm_set_hotword_model(struct cras_use_case_mgr *mgr, const char *model)
+void ucm_disable_all_hotword_models(struct cras_use_case_mgr *mgr)
{
const char **list;
int num_enmods, mod_idx;
- char *model_mod = NULL;
+
+ if (!mgr)
+ return;
+
+ /* Disable all currently enabled hotword model modifiers. */
+ num_enmods = snd_use_case_get_list(mgr->mgr, "_enamods", &list);
+ if (num_enmods <= 0)
+ return;
+
+ for (mod_idx = 0; mod_idx < num_enmods; mod_idx++) {
+ if (!strncmp(list[mod_idx], hotword_model_prefix,
+ strlen(hotword_model_prefix)))
+ ucm_set_modifier_enabled(mgr, list[mod_idx], 0);
+ }
+ snd_use_case_free_list(list, num_enmods);
+}
+
+int ucm_enable_hotword_model(struct cras_use_case_mgr *mgr)
+{
+ if (mgr->hotword_modifier)
+ return ucm_set_modifier_enabled(mgr, mgr->hotword_modifier, 1);
+ return -EINVAL;
+}
+
+static int ucm_is_modifier_enabled(struct cras_use_case_mgr *mgr,
+ char *modifier, long *value)
+{
+ int rc;
+ char *id;
+ size_t len = strlen(modifier) + 11 + 1;
+
+ id = (char *)malloc(len);
+
+ if (!id)
+ return -ENOMEM;
+
+ snprintf(id, len, "_modstatus/%s", modifier);
+ rc = snd_use_case_geti(mgr->mgr, id, value);
+ free(id);
+ return rc;
+}
+
+int ucm_set_hotword_model(struct cras_use_case_mgr *mgr, const char *model)
+{
+ char *model_mod;
+ long mod_status = 0;
size_t model_mod_size =
strlen(model) + 1 + strlen(hotword_model_prefix) + 1;
+
model_mod = (char *)malloc(model_mod_size);
+
if (!model_mod)
return -ENOMEM;
snprintf(model_mod, model_mod_size, "%s %s", hotword_model_prefix,
@@ -1001,21 +1121,16 @@ int ucm_set_hotword_model(struct cras_use_case_mgr *mgr, const char *model)
return -EINVAL;
}
- /* Disable all currently enabled horword model modifiers. */
- num_enmods = snd_use_case_get_list(mgr->mgr, "_enamods", &list);
- if (num_enmods <= 0)
- goto enable_mod;
-
- for (mod_idx = 0; mod_idx < num_enmods; mod_idx++) {
- if (!strncmp(list[mod_idx], hotword_model_prefix,
- strlen(hotword_model_prefix)))
- ucm_set_modifier_enabled(mgr, list[mod_idx], 0);
- }
- snd_use_case_free_list(list, num_enmods);
+ /* If check failed, just move on, dont fail incoming model */
+ if (mgr->hotword_modifier)
+ ucm_is_modifier_enabled(mgr, mgr->hotword_modifier,
+ &mod_status);
-enable_mod:
- ucm_set_modifier_enabled(mgr, model_mod, 1);
- free((void *)model_mod);
+ ucm_disable_all_hotword_models(mgr);
+ free(mgr->hotword_modifier);
+ mgr->hotword_modifier = model_mod;
+ if (mod_status)
+ return ucm_enable_hotword_model(mgr);
return 0;
}
diff --git a/cras/src/server/cras_alsa_ucm.h b/cras/src/server/cras_alsa_ucm.h
index 99a8b440..55c3cf62 100644
--- a/cras/src/server/cras_alsa_ucm.h
+++ b/cras/src/server/cras_alsa_ucm.h
@@ -67,6 +67,28 @@ int ucm_swap_mode_exists(struct cras_use_case_mgr *mgr);
int ucm_enable_swap_mode(struct cras_use_case_mgr *mgr, const char *node_name,
int enable);
+/* Checks if modifier of noise cancellation for given node_name exists in ucm.
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * node_name - The node name.
+ * Returns:
+ * 1 if it exists, 0 otherwise.
+ */
+int ucm_node_noise_cancellation_exists(struct cras_use_case_mgr *mgr,
+ const char *node_name);
+
+/* Enables or disables noise cancellation for the given node_name. First checks
+ * if the modifier is already enabled or disabled.
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * node_name - The node name.
+ * enable - Enable device if non-zero.
+ * Returns:
+ * 0 on success or negative error code on failure.
+ */
+int ucm_enable_node_noise_cancellation(struct cras_use_case_mgr *mgr,
+ const char *node_name, int enable);
+
/* Enables or disables a UCM device. First checks if the device is already
* enabled or disabled.
* Args:
@@ -306,11 +328,26 @@ char *ucm_get_hotword_models(struct cras_use_case_mgr *mgr);
/* Sets the desired hotword model.
* Args:
* mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * model - locale for model
* Returns:
* 0 on success or negative error code on failure.
*/
int ucm_set_hotword_model(struct cras_use_case_mgr *mgr, const char *model);
+/* Enable previously set hotword modifier
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * Returns:
+ * 0 on success or negative error code on failure.
+ */
+int ucm_enable_hotword_model(struct cras_use_case_mgr *mgr);
+
+/* Disable all hotword model modifiers
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ */
+void ucm_disable_all_hotword_models(struct cras_use_case_mgr *mgr);
+
/* Checks if this card has fully specified UCM config.
*
* Args:
diff --git a/cras/src/server/cras_apm_list.c b/cras/src/server/cras_apm_list.c
index ac57d86a..ab891137 100644
--- a/cras/src/server/cras_apm_list.c
+++ b/cras/src/server/cras_apm_list.c
@@ -61,9 +61,10 @@
* stream.
* work_queue - A task queue instance created and destroyed by
* libwebrtc_apm.
- * use_tuned_settings - True if this APM uses settings tuned specifically
- * for this hardware in AEC use case. Otherwise it uses the generic
- * settings like run inside browser.
+ * is_aec_use_case - True if the input and output devices pair is in the
+ * typical AEC use case. This flag decides whether to use settings
+ * tuned specifically for this hardware if exists. Otherwise it uses
+ * the generic settings like run inside browser.
*/
struct cras_apm {
webrtc_apm apm_ptr;
@@ -74,7 +75,7 @@ struct cras_apm {
struct cras_audio_format fmt;
struct cras_audio_area *area;
void *work_queue;
- bool use_tuned_settings;
+ bool is_aec_use_case;
struct cras_apm *prev, *next;
};
@@ -239,13 +240,12 @@ static void get_best_channels(struct cras_audio_format *apm_fmt)
int ch;
int8_t layout[CRAS_CH_MAX];
- /* Assume device format has correct channel layout populated. */
- if (apm_fmt->num_channels <= 2)
- return;
-
- /* If the device provides recording from more channels than we care
- * about, construct a new channel layout containing subset of original
- * channels that matches either FL, FR, or FC.
+ /* Using the format from dev_fmt is dangerous because input device
+ * could have wild configurations like unuse the 1st channel and
+ * connects 2nd channel to the only mic. Data in the first channel
+ * is what APM cares about so always construct a new channel layout
+ * containing subset of original channels that matches either FL, FR,
+ * or FC.
* TODO(hychao): extend the logic when we have a stream that wants
* to record channels like RR(rear right).
*/
@@ -290,16 +290,16 @@ struct cras_apm *cras_apm_list_add_apm(struct cras_apm_list *list,
/* Use tuned settings only when the forward dev(capture) and reverse
* dev(playback) both are in typical AEC use case. */
- apm->use_tuned_settings = is_aec_use_case;
+ apm->is_aec_use_case = is_aec_use_case;
if (rmodule->odev) {
- apm->use_tuned_settings &=
+ apm->is_aec_use_case &=
cras_iodev_is_aec_use_case(rmodule->odev->active_node);
}
/* Use the configs tuned specifically for internal device. Otherwise
* just pass NULL so every other settings will be default. */
apm->apm_ptr =
- apm->use_tuned_settings ?
+ apm->is_aec_use_case ?
webrtc_apm_create(apm->fmt.num_channels,
apm->fmt.frame_rate, aec_ini,
apm_ini) :
@@ -691,7 +691,9 @@ struct cras_audio_format *cras_apm_list_get_format(struct cras_apm *apm)
bool cras_apm_list_get_use_tuned_settings(struct cras_apm *apm)
{
- return apm->use_tuned_settings;
+ /* If input and output devices in AEC use case, plus that a
+ * tuned setting is provided. */
+ return apm->is_aec_use_case && (aec_ini || apm_ini);
}
void cras_apm_list_set_aec_dump(struct cras_apm_list *list, void *dev_ptr,
diff --git a/cras/src/server/cras_bt_battery_provider.c b/cras/src/server/cras_bt_battery_provider.c
new file mode 100644
index 00000000..13e6590f
--- /dev/null
+++ b/cras/src/server/cras_bt_battery_provider.c
@@ -0,0 +1,371 @@
+/* Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include <dbus/dbus.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "cras_bt_adapter.h"
+#include "cras_bt_battery_provider.h"
+#include "cras_bt_constants.h"
+#include "cras_dbus_util.h"
+#include "cras_observer.h"
+#include "utlist.h"
+
+/* CRAS registers one battery provider to BlueZ, so we use a singleton. */
+static struct cras_bt_battery_provider battery_provider = {
+ .object_path = CRAS_DEFAULT_BATTERY_PROVIDER,
+ .interface = BLUEZ_INTERFACE_BATTERY_PROVIDER,
+ .conn = NULL,
+ .is_registered = false,
+ .observer = NULL,
+ .batteries = NULL,
+};
+
+static int cmp_battery_address(const struct cras_bt_battery *battery,
+ const char *address)
+{
+ return strcmp(battery->address, address);
+}
+
+static void replace_colon_with_underscore(char *str)
+{
+ for (int i = 0; str[i]; i++) {
+ if (str[i] == ':')
+ str[i] = '_';
+ }
+}
+
+/* Converts address XX:XX:XX:XX:XX:XX to Battery Provider object path:
+ * /org/chromium/Cras/Bluetooth/BatteryProvider/XX_XX_XX_XX_XX_XX
+ */
+static char *address_to_battery_path(const char *address)
+{
+ char *object_path = malloc(strlen(CRAS_DEFAULT_BATTERY_PROVIDER) +
+ strlen(address) + 2);
+
+ sprintf(object_path, "%s/%s", CRAS_DEFAULT_BATTERY_PROVIDER, address);
+ replace_colon_with_underscore(object_path);
+
+ return object_path;
+}
+
+/* Converts address XX:XX:XX:XX:XX:XX to device object path:
+ * /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX
+ */
+static char *address_to_device_path(const char *address)
+{
+ char *object_path = malloc(strlen(CRAS_DEFAULT_BATTERY_PREFIX) +
+ strlen(address) + 1);
+
+ sprintf(object_path, "%s%s", CRAS_DEFAULT_BATTERY_PREFIX, address);
+ replace_colon_with_underscore(object_path);
+
+ return object_path;
+}
+
+static struct cras_bt_battery *battery_new(const char *address, uint32_t level)
+{
+ struct cras_bt_battery *battery;
+
+ battery = calloc(1, sizeof(struct cras_bt_battery));
+ battery->address = strdup(address);
+ battery->object_path = address_to_battery_path(address);
+ battery->device_path = address_to_device_path(address);
+ battery->level = level;
+
+ return battery;
+}
+
+static void battery_free(struct cras_bt_battery *battery)
+{
+ if (battery->address)
+ free(battery->address);
+ if (battery->object_path)
+ free(battery->object_path);
+ if (battery->device_path)
+ free(battery->device_path);
+ free(battery);
+}
+
+static void populate_battery_properties(DBusMessageIter *iter,
+ const struct cras_bt_battery *battery)
+{
+ DBusMessageIter dict, entry, variant;
+ const char *property_percentage = "Percentage";
+ const char *property_device = "Device";
+ uint8_t level = battery->level;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
+
+ dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL,
+ &entry);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
+ &property_percentage);
+ dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_BYTE_AS_STRING, &variant);
+ dbus_message_iter_append_basic(&variant, DBUS_TYPE_BYTE, &level);
+ dbus_message_iter_close_container(&entry, &variant);
+ dbus_message_iter_close_container(&dict, &entry);
+
+ dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL,
+ &entry);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
+ &property_device);
+ dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_OBJECT_PATH_AS_STRING,
+ &variant);
+ dbus_message_iter_append_basic(&variant, DBUS_TYPE_OBJECT_PATH,
+ &battery->device_path);
+ dbus_message_iter_close_container(&entry, &variant);
+ dbus_message_iter_close_container(&dict, &entry);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+/* Creates a new battery object and exposes it on D-Bus. */
+static struct cras_bt_battery *
+get_or_create_battery(struct cras_bt_battery_provider *provider,
+ const char *address, uint32_t level)
+{
+ struct cras_bt_battery *battery;
+ DBusMessage *msg;
+ DBusMessageIter iter, dict, entry;
+
+ LL_SEARCH(provider->batteries, battery, address, cmp_battery_address);
+
+ if (battery)
+ return battery;
+
+ syslog(LOG_DEBUG, "Creating new battery for %s", address);
+
+ battery = battery_new(address, level);
+ LL_APPEND(provider->batteries, battery);
+
+ msg = dbus_message_new_signal(CRAS_DEFAULT_BATTERY_PROVIDER,
+ DBUS_INTERFACE_OBJECT_MANAGER,
+ DBUS_SIGNAL_INTERFACES_ADDED);
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &battery->object_path);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sa{sv}}",
+ &dict);
+ dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL,
+ &entry);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
+ &provider->interface);
+ populate_battery_properties(&entry, battery);
+ dbus_message_iter_close_container(&dict, &entry);
+ dbus_message_iter_close_container(&iter, &dict);
+
+ if (!dbus_connection_send(provider->conn, msg, NULL)) {
+ syslog(LOG_ERR,
+ "Error sending " DBUS_SIGNAL_INTERFACES_ADDED " signal");
+ }
+
+ dbus_message_unref(msg);
+
+ return battery;
+}
+
+/* Updates the level of a battery object and signals it on D-Bus. */
+static void
+update_battery_level(const struct cras_bt_battery_provider *provider,
+ struct cras_bt_battery *battery, uint32_t level)
+{
+ DBusMessage *msg;
+ DBusMessageIter iter;
+
+ if (battery->level == level)
+ return;
+
+ battery->level = level;
+
+ msg = dbus_message_new_signal(battery->object_path,
+ DBUS_INTERFACE_PROPERTIES,
+ DBUS_SIGNAL_PROPERTIES_CHANGED);
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+ &provider->interface);
+ populate_battery_properties(&iter, battery);
+
+ if (!dbus_connection_send(provider->conn, msg, NULL)) {
+ syslog(LOG_ERR, "Error sending " DBUS_SIGNAL_PROPERTIES_CHANGED
+ " signal");
+ }
+
+ dbus_message_unref(msg);
+}
+
+/* Invoked when HFP sends an alert about a battery value change. */
+static void on_bt_battery_changed(void *context, const char *address,
+ uint32_t level)
+{
+ struct cras_bt_battery_provider *provider = context;
+
+ syslog(LOG_DEBUG, "Battery changed for address %s, level %d", address,
+ level);
+
+ if (!provider->is_registered) {
+ syslog(LOG_WARNING, "Received battery level update while "
+ "battery provider is not registered");
+ return;
+ }
+
+ struct cras_bt_battery *battery =
+ get_or_create_battery(provider, address, level);
+
+ update_battery_level(provider, battery, level);
+}
+
+/* Invoked when we receive a D-Bus return of RegisterBatteryProvider from
+ * BlueZ.
+ */
+static void
+cras_bt_on_battery_provider_registered(DBusPendingCall *pending_call,
+ void *data)
+{
+ DBusMessage *reply;
+ struct cras_bt_battery_provider *provider = data;
+ struct cras_observer_ops observer_ops;
+
+ reply = dbus_pending_call_steal_reply(pending_call);
+ dbus_pending_call_unref(pending_call);
+
+ if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+ syslog(LOG_ERR, "RegisterBatteryProvider returned error: %s",
+ dbus_message_get_error_name(reply));
+ dbus_message_unref(reply);
+ return;
+ }
+
+ syslog(LOG_INFO, "RegisterBatteryProvider succeeded");
+
+ provider->is_registered = true;
+
+ memset(&observer_ops, 0, sizeof(observer_ops));
+ observer_ops.bt_battery_changed = on_bt_battery_changed;
+ provider->observer = cras_observer_add(&observer_ops, provider);
+
+ dbus_message_unref(reply);
+}
+
+int cras_bt_register_battery_provider(DBusConnection *conn,
+ const struct cras_bt_adapter *adapter)
+{
+ const char *adapter_path;
+ DBusMessage *method_call;
+ DBusMessageIter message_iter;
+ DBusPendingCall *pending_call;
+
+ if (battery_provider.is_registered) {
+ syslog(LOG_ERR, "Battery Provider already registered");
+ return -EBUSY;
+ }
+
+ if (battery_provider.conn)
+ dbus_connection_unref(battery_provider.conn);
+
+ battery_provider.conn = conn;
+ dbus_connection_ref(battery_provider.conn);
+
+ adapter_path = cras_bt_adapter_object_path(adapter);
+ method_call = dbus_message_new_method_call(
+ BLUEZ_SERVICE, adapter_path,
+ BLUEZ_INTERFACE_BATTERY_PROVIDER_MANAGER,
+ "RegisterBatteryProvider");
+ if (!method_call)
+ return -ENOMEM;
+
+ dbus_message_iter_init_append(method_call, &message_iter);
+ dbus_message_iter_append_basic(&message_iter, DBUS_TYPE_OBJECT_PATH,
+ &battery_provider.object_path);
+
+ if (!dbus_connection_send_with_reply(conn, method_call, &pending_call,
+ DBUS_TIMEOUT_USE_DEFAULT)) {
+ dbus_message_unref(method_call);
+ return -ENOMEM;
+ }
+
+ dbus_message_unref(method_call);
+
+ if (!pending_call)
+ return -EIO;
+
+ if (!dbus_pending_call_set_notify(
+ pending_call, cras_bt_on_battery_provider_registered,
+ &battery_provider, NULL)) {
+ dbus_pending_call_cancel(pending_call);
+ dbus_pending_call_unref(pending_call);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/* Removes a battery object and signals the removal on D-Bus as well. */
+static void cleanup_battery(struct cras_bt_battery_provider *provider,
+ struct cras_bt_battery *battery)
+{
+ DBusMessage *msg;
+ DBusMessageIter iter, entry;
+
+ if (!battery)
+ return;
+
+ LL_DELETE(provider->batteries, battery);
+
+ msg = dbus_message_new_signal(CRAS_DEFAULT_BATTERY_PROVIDER,
+ DBUS_INTERFACE_OBJECT_MANAGER,
+ DBUS_SIGNAL_INTERFACES_REMOVED);
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &battery->object_path);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &entry);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
+ &provider->interface);
+ dbus_message_iter_close_container(&iter, &entry);
+
+ if (!dbus_connection_send(provider->conn, msg, NULL)) {
+ syslog(LOG_ERR, "Error sending " DBUS_SIGNAL_INTERFACES_REMOVED
+ " signal");
+ }
+
+ dbus_message_unref(msg);
+
+ battery_free(battery);
+}
+
+void cras_bt_battery_provider_reset()
+{
+ struct cras_bt_battery *battery;
+
+ syslog(LOG_INFO, "Resetting battery provider");
+
+ if (!battery_provider.is_registered)
+ return;
+
+ battery_provider.is_registered = false;
+
+ LL_FOREACH (battery_provider.batteries, battery) {
+ cleanup_battery(&battery_provider, battery);
+ }
+
+ if (battery_provider.conn) {
+ dbus_connection_unref(battery_provider.conn);
+ battery_provider.conn = NULL;
+ }
+
+ if (battery_provider.observer) {
+ cras_observer_remove(battery_provider.observer);
+ battery_provider.observer = NULL;
+ }
+}
diff --git a/cras/src/server/cras_bt_battery_provider.h b/cras/src/server/cras_bt_battery_provider.h
new file mode 100644
index 00000000..1998cd78
--- /dev/null
+++ b/cras/src/server/cras_bt_battery_provider.h
@@ -0,0 +1,47 @@
+/* Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef CRAS_BT_BATTERY_PROVIDER_H_
+#define CRAS_BT_BATTERY_PROVIDER_H_
+
+#include <dbus/dbus.h>
+#include <stdbool.h>
+
+#include "cras_bt_adapter.h"
+
+/* Object to represent a battery that is exposed to BlueZ. */
+struct cras_bt_battery {
+ char *address;
+ char *object_path;
+ char *device_path;
+ uint32_t level;
+ struct cras_bt_battery *next;
+};
+
+/* Object to register as battery provider so that bluetoothd will monitor
+ * battery objects that we expose.
+ */
+struct cras_bt_battery_provider {
+ const char *object_path;
+ const char *interface;
+ DBusConnection *conn;
+ bool is_registered;
+ struct cras_observer_client *observer;
+ struct cras_bt_battery *batteries;
+};
+
+/* Registers battery provider to bluetoothd. This is used when a Bluetooth
+ * adapter got enumerated.
+ * Args:
+ * conn - The D-Bus connection.
+ * adapter - The enumerated bluetooth adapter.
+ */
+int cras_bt_register_battery_provider(DBusConnection *conn,
+ const struct cras_bt_adapter *adapter);
+
+/* Resets internal state of battery provider. */
+void cras_bt_battery_provider_reset();
+
+#endif /* CRAS_BT_BATTERY_PROVIDER_H_ */
diff --git a/cras/src/server/cras_bt_constants.h b/cras/src/server/cras_bt_constants.h
index 618ac872..318aecab 100644
--- a/cras/src/server/cras_bt_constants.h
+++ b/cras/src/server/cras_bt_constants.h
@@ -9,6 +9,9 @@
#define BLUEZ_SERVICE "org.bluez"
#define BLUEZ_INTERFACE_ADAPTER "org.bluez.Adapter1"
+#define BLUEZ_INTERFACE_BATTERY_PROVIDER "org.bluez.BatteryProvider1"
+#define BLUEZ_INTERFACE_BATTERY_PROVIDER_MANAGER \
+ "org.bluez.BatteryProviderManager1"
#define BLUEZ_INTERFACE_DEVICE "org.bluez.Device1"
#define BLUEZ_INTERFACE_MEDIA "org.bluez.Media1"
#define BLUEZ_INTERFACE_MEDIA_ENDPOINT "org.bluez.MediaEndpoint1"
@@ -21,6 +24,9 @@
#ifndef DBUS_INTERFACE_OBJECT_MANAGER
#define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager"
#endif
+#define DBUS_SIGNAL_INTERFACES_ADDED "InterfacesAdded"
+#define DBUS_SIGNAL_INTERFACES_REMOVED "InterfacesRemoved"
+#define DBUS_SIGNAL_PROPERTIES_CHANGED "PropertiesChanged"
/* UUIDs taken from lib/uuid.h in the BlueZ source */
#define HSP_HS_UUID "00001108-0000-1000-8000-00805f9b34fb"
@@ -49,6 +55,10 @@
#define CRAS_PLAYER_IDENTITY_DEFAULT "DefaultPlayer"
#define CRAS_PLAYER_METADATA_SIZE_MAX 128 * sizeof(char)
+#define CRAS_DEFAULT_BATTERY_PROVIDER \
+ "/org/chromium/Cras/Bluetooth/BatteryProvider"
+#define CRAS_DEFAULT_BATTERY_PREFIX "/org/bluez/hci0/dev_"
+
/* Instead of letting CRAS obtain the A2DP streaming packet size (a.k.a. AVDTP
* MTU) from BlueZ Media Transport, force the packet size to the default L2CAP
* packet size. This prevent the audio peripheral device to negotiate a larger
diff --git a/cras/src/server/cras_bt_device.c b/cras/src/server/cras_bt_device.c
index 70c87479..6b06dd13 100644
--- a/cras/src/server/cras_bt_device.c
+++ b/cras/src/server/cras_bt_device.c
@@ -34,6 +34,7 @@
#include "cras_server_metrics.h"
#include "cras_system_state.h"
#include "cras_tm.h"
+#include "sfh.h"
#include "utlist.h"
/*
@@ -91,6 +92,7 @@ static const unsigned int CRAS_SUPPORTED_PROFILES =
* sco_fd - The file descriptor of the SCO connection.
* sco_ref_count - The reference counts of the SCO connection.
* suspend_reason - The reason code for why suspend is scheduled.
+ * stable_id - The unique and persistent id of this bt_device.
*/
struct cras_bt_device {
DBusConnection *conn;
@@ -115,6 +117,7 @@ struct cras_bt_device {
int sco_fd;
size_t sco_ref_count;
enum cras_bt_device_suspend_reason suspend_reason;
+ unsigned int stable_id;
struct cras_bt_device *prev, *next;
};
@@ -174,6 +177,9 @@ struct cras_bt_device *cras_bt_device_create(DBusConnection *conn,
free(device);
return NULL;
}
+ device->stable_id =
+ SuperFastHash(device->object_path, strlen(device->object_path),
+ strlen(device->object_path));
DL_APPEND(devices, device);
@@ -343,6 +349,11 @@ const char *cras_bt_device_object_path(const struct cras_bt_device *device)
return device->object_path;
}
+int cras_bt_device_get_stable_id(const struct cras_bt_device *device)
+{
+ return device->stable_id;
+}
+
struct cras_bt_adapter *
cras_bt_device_adapter(const struct cras_bt_device *device)
{
@@ -704,10 +715,14 @@ static void bt_device_cancel_suspend(struct cras_bt_device *device);
void cras_bt_device_set_connected(struct cras_bt_device *device, int value)
{
struct cras_tm *tm = cras_system_state_get_tm();
- if (device->connected || value)
- BTLOG(btlog, BT_DEV_CONNECTED_CHANGE, device->profiles, value);
+ if (!device->connected && value) {
+ BTLOG(btlog, BT_DEV_CONNECTED, device->profiles,
+ device->stable_id);
+ }
if (device->connected && !value) {
+ BTLOG(btlog, BT_DEV_DISCONNECTED, device->profiles,
+ device->stable_id);
cras_bt_profile_on_device_disconnected(device);
/* Device is disconnected, resets connected profiles and the
* suspend timer which scheduled earlier. */
diff --git a/cras/src/server/cras_bt_device.h b/cras/src/server/cras_bt_device.h
index 4202bc93..9d3a2b9e 100644
--- a/cras/src/server/cras_bt_device.h
+++ b/cras/src/server/cras_bt_device.h
@@ -50,6 +50,10 @@ void cras_bt_device_reset();
struct cras_bt_device *cras_bt_device_get(const char *object_path);
const char *cras_bt_device_object_path(const struct cras_bt_device *device);
+
+/* Gets the stable id of given cras_bt_device. */
+int cras_bt_device_get_stable_id(const struct cras_bt_device *device);
+
struct cras_bt_adapter *
cras_bt_device_adapter(const struct cras_bt_device *device);
const char *cras_bt_device_address(const struct cras_bt_device *device);
diff --git a/cras/src/server/cras_bt_io.c b/cras/src/server/cras_bt_io.c
index 9f5c2f79..acdca809 100644
--- a/cras/src/server/cras_bt_io.c
+++ b/cras/src/server/cras_bt_io.c
@@ -527,10 +527,7 @@ struct cras_iodev *cras_bt_io_create(struct cras_bt_device *device,
active->base.idx = btio->next_node_id++;
active->base.type = dev->active_node->type;
active->base.volume = 100;
- active->base.stable_id =
- SuperFastHash(cras_bt_device_object_path(device),
- strlen(cras_bt_device_object_path(device)),
- strlen(cras_bt_device_object_path(device)));
+ active->base.stable_id = cras_bt_device_get_stable_id(device);
active->base.ui_gain_scaler = 1.0f;
/*
* If the same headset is connected in wideband mode, we shall assign
diff --git a/cras/src/server/cras_bt_manager.c b/cras/src/server/cras_bt_manager.c
index 77e8079c..a7103406 100644
--- a/cras/src/server/cras_bt_manager.c
+++ b/cras/src/server/cras_bt_manager.c
@@ -19,6 +19,7 @@
#include "cras_bt_player.h"
#include "cras_bt_profile.h"
#include "cras_bt_transport.h"
+#include "cras_bt_battery_provider.h"
#include "utlist.h"
struct cras_bt_event_log *btlog;
@@ -120,6 +121,32 @@ static void cras_bt_interface_added(DBusConnection *conn,
object_path);
}
}
+ } else if (strcmp(interface_name,
+ BLUEZ_INTERFACE_BATTERY_PROVIDER_MANAGER) == 0) {
+ struct cras_bt_adapter *adapter;
+ int ret;
+
+ syslog(LOG_INFO,
+ "Bluetooth Battery Provider Manager available");
+
+ adapter = cras_bt_adapter_get(object_path);
+ if (adapter) {
+ syslog(LOG_INFO,
+ "Registering Battery Provider for adapter %s",
+ cras_bt_adapter_address(adapter));
+ ret = cras_bt_register_battery_provider(conn, adapter);
+ if (ret != 0) {
+ syslog(LOG_ERR,
+ "Error registering Battery Provider "
+ "for adapter %s: %s",
+ cras_bt_adapter_address(adapter),
+ strerror(-ret));
+ }
+ } else {
+ syslog(LOG_WARNING,
+ "Adapter not available when trying to create "
+ "Battery Provider");
+ }
}
}
@@ -158,6 +185,10 @@ static void cras_bt_interface_removed(DBusConnection *conn,
cras_bt_transport_object_path(transport));
cras_bt_transport_remove(transport);
}
+ } else if (strcmp(interface_name,
+ BLUEZ_INTERFACE_BATTERY_PROVIDER_MANAGER) == 0) {
+ syslog(LOG_INFO, "Bluetooth Battery Provider Manager removed");
+ cras_bt_battery_provider_reset();
}
}
diff --git a/cras/src/server/cras_dbus_control.c b/cras/src/server/cras_dbus_control.c
index 3479c3c6..b66e1276 100644
--- a/cras/src/server/cras_dbus_control.c
+++ b/cras/src/server/cras_dbus_control.c
@@ -125,6 +125,9 @@
" <method name=\"SetWbsEnabled\">\n" \
" <arg name=\"enabled\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
+ " <method name=\"SetNoiseCancellationEnabled\">\n" \
+ " <arg name=\"enabled\" type=\"b\" direction=\"in\"/>\n" \
+ " </method>\n" \
" <method name=\"SetPlayerPlaybackStatus\">\n" \
" <arg name=\"status\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
@@ -137,8 +140,6 @@
" <method name=\"SetPlayerMetadata\">\n" \
" <arg name=\"metadata\" type=\"a{sv}\" direction=\"in\"/>\n" \
" </method>\n" \
- " <method name=\"ResendBluetoothBattery\">\n" \
- " </method>\n" \
" </interface>\n" \
" <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n" \
" <method name=\"Introspect\">\n" \
@@ -960,6 +961,24 @@ static DBusHandlerResult handle_set_wbs_enabled(DBusConnection *conn,
return DBUS_HANDLER_RESULT_HANDLED;
}
+static DBusHandlerResult
+handle_set_noise_cancellation_enabled(DBusConnection *conn,
+ DBusMessage *message, void *arg)
+{
+ int rc;
+ dbus_bool_t enabled;
+
+ rc = get_single_arg(message, DBUS_TYPE_BOOLEAN, &enabled);
+ if (rc)
+ return rc;
+
+ cras_system_set_noise_cancellation_enabled(enabled);
+
+ send_empty_reply(conn, message);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
static DBusHandlerResult handle_set_player_playback_status(DBusConnection *conn,
DBusMessage *message,
void *arg)
@@ -1060,17 +1079,6 @@ static DBusHandlerResult handle_set_player_metadata(DBusConnection *conn,
return DBUS_HANDLER_RESULT_HANDLED;
}
-static DBusHandlerResult handle_resend_bluetooth_battery(DBusConnection *conn,
- DBusMessage *message,
- void *arg)
-{
- cras_hfp_ag_resend_device_battery_level();
-
- send_empty_reply(conn, message);
-
- return DBUS_HANDLER_RESULT_HANDLED;
-}
-
/* Handle incoming messages. */
static DBusHandlerResult handle_control_message(DBusConnection *conn,
DBusMessage *message, void *arg)
@@ -1199,6 +1207,10 @@ static DBusHandlerResult handle_control_message(DBusConnection *conn,
"SetWbsEnabled")) {
return handle_set_wbs_enabled(conn, message, arg);
} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
+ "SetNoiseCancellationEnabled")) {
+ return handle_set_noise_cancellation_enabled(conn, message,
+ arg);
+ } else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
"SetPlayerPlaybackStatus")) {
return handle_set_player_playback_status(conn, message, arg);
} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
@@ -1210,9 +1222,6 @@ static DBusHandlerResult handle_control_message(DBusConnection *conn,
} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
"SetPlayerMetadata")) {
return handle_set_player_metadata(conn, message, arg);
- } else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
- "ResendBluetoothBattery")) {
- return handle_resend_bluetooth_battery(conn, message, arg);
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@@ -1324,8 +1333,8 @@ static void signal_active_node_changed(void *context,
dbus_uint32_t serial = 0;
msg = create_dbus_message((dir == CRAS_STREAM_OUTPUT) ?
- "ActiveOutputNodeChanged" :
- "ActiveInputNodeChanged");
+ "ActiveOutputNodeChanged" :
+ "ActiveInputNodeChanged");
if (!msg)
return;
dbus_message_append_args(msg, DBUS_TYPE_UINT64, &node_id,
@@ -1463,25 +1472,6 @@ static void signal_non_empty_audio_state_changed(void *context, int non_empty)
dbus_message_unref(msg);
}
-static void signal_bt_battery_changed(void *context, const char *address,
- uint32_t level)
-{
- struct cras_dbus_control *control = (struct cras_dbus_control *)context;
- dbus_uint32_t serial = 0;
- DBusMessage *msg;
-
- msg = create_dbus_message("BluetoothBatteryChanged");
- if (!msg)
- return;
-
- dbus_message_append_args(msg, DBUS_TYPE_STRING, &address,
- DBUS_TYPE_INVALID);
- dbus_message_append_args(msg, DBUS_TYPE_UINT32, &level,
- DBUS_TYPE_INVALID);
- dbus_connection_send(control->conn, msg, &serial);
- dbus_message_unref(msg);
-}
-
/* Exported Interface */
void cras_dbus_control_start(DBusConnection *conn)
@@ -1523,7 +1513,6 @@ void cras_dbus_control_start(DBusConnection *conn)
observer_ops.hotword_triggered = signal_hotword_triggered;
observer_ops.non_empty_audio_state_changed =
signal_non_empty_audio_state_changed;
- observer_ops.bt_battery_changed = signal_bt_battery_changed;
dbus_control.observer = cras_observer_add(&observer_ops, &dbus_control);
}
diff --git a/cras/src/server/cras_device_monitor.c b/cras/src/server/cras_device_monitor.c
index 7dd0f5d7..e9730a0b 100644
--- a/cras/src/server/cras_device_monitor.c
+++ b/cras/src/server/cras_device_monitor.c
@@ -13,6 +13,7 @@
enum CRAS_DEVICE_MONITOR_MSG_TYPE {
RESET_DEVICE,
SET_MUTE_STATE,
+ ERROR_CLOSE,
};
struct cras_device_monitor_message {
@@ -62,6 +63,21 @@ int cras_device_monitor_set_device_mute_state(unsigned int dev_idx)
return 0;
}
+int cras_device_monitor_error_close(unsigned int dev_idx)
+{
+ struct cras_device_monitor_message msg;
+ int err;
+
+ init_device_msg(&msg, ERROR_CLOSE, dev_idx);
+ err = cras_main_message_send((struct cras_main_message *)&msg);
+ if (err < 0) {
+ syslog(LOG_ERR, "Failed to send device message %d",
+ ERROR_CLOSE);
+ return err;
+ }
+ return 0;
+}
+
/* When device is in a bad state, e.g. severe underrun,
* it might break how audio thread works and cause busy wake up loop.
* Resetting the device can bring device back to normal state.
@@ -84,6 +100,10 @@ static void handle_device_message(struct cras_main_message *msg, void *arg)
case SET_MUTE_STATE:
cras_iodev_list_set_dev_mute(device_msg->dev_idx);
break;
+ case ERROR_CLOSE:
+ syslog(LOG_ERR, "Close erroneous device in main thread");
+ cras_iodev_list_suspend_dev(device_msg->dev_idx);
+ break;
default:
syslog(LOG_ERR, "Unknown device message type %u",
device_msg->message_type);
diff --git a/cras/src/server/cras_device_monitor.h b/cras/src/server/cras_device_monitor.h
index ac31adb9..eca2372b 100644
--- a/cras/src/server/cras_device_monitor.h
+++ b/cras/src/server/cras_device_monitor.h
@@ -15,4 +15,8 @@ int cras_device_monitor_set_device_mute_state(unsigned int dev_idx);
/* Initializes device monitor and sets main thread callback. */
int cras_device_monitor_init();
+/* Asks main thread to close device because error has occured in audio
+ * thread. */
+int cras_device_monitor_error_close(unsigned int dev_idx);
+
#endif /* CRAS_DEVICE_MONITOR_H_ */
diff --git a/cras/src/server/cras_fmt_conv.c b/cras/src/server/cras_fmt_conv.c
index 509db1eb..842529b9 100644
--- a/cras/src/server/cras_fmt_conv.c
+++ b/cras/src/server/cras_fmt_conv.c
@@ -216,6 +216,19 @@ static size_t stereo_to_51(struct cras_fmt_conv *conv, const uint8_t *in,
return s16_stereo_to_51(left, right, center, in, in_frames, out);
}
+static size_t quad_to_51(struct cras_fmt_conv *conv, const uint8_t *in,
+ size_t in_frames, uint8_t *out)
+{
+ size_t fl, fr, rl, rr;
+
+ fl = conv->out_fmt.channel_layout[CRAS_CH_FL];
+ fr = conv->out_fmt.channel_layout[CRAS_CH_FR];
+ rl = conv->out_fmt.channel_layout[CRAS_CH_RL];
+ rr = conv->out_fmt.channel_layout[CRAS_CH_RR];
+
+ return s16_quad_to_51(fl, fr, rl, rr, in, in_frames, out);
+}
+
static size_t _51_to_stereo(struct cras_fmt_conv *conv, const uint8_t *in,
size_t in_frames, uint8_t *out)
{
@@ -398,6 +411,8 @@ struct cras_fmt_conv *cras_fmt_conv_create(const struct cras_audio_format *in,
conv->channel_converter = quad_to_stereo;
} else if (in->num_channels == 2 && out->num_channels == 6) {
conv->channel_converter = stereo_to_51;
+ } else if (in->num_channels == 4 && out->num_channels == 6) {
+ conv->channel_converter = quad_to_51;
} else if (in->num_channels == 6 &&
(out->num_channels == 2 || out->num_channels == 4)) {
int in_channel_layout_set = 0;
diff --git a/cras/src/server/cras_fmt_conv_ops.c b/cras/src/server/cras_fmt_conv_ops.c
index a306d216..adc55215 100644
--- a/cras/src/server/cras_fmt_conv_ops.c
+++ b/cras/src/server/cras_fmt_conv_ops.c
@@ -223,6 +223,44 @@ size_t s16_stereo_to_51(size_t left, size_t right, size_t center,
}
/*
+ * Channel converter: quad to 5.1 surround.
+ *
+ * Fit the front left/right of input to the front left/right of output
+ * and rear left/right of input to the rear left/right of output
+ * respectively and fill others with zero.
+ */
+size_t s16_quad_to_51(size_t font_left, size_t front_right, size_t rear_left,
+ size_t rear_right, const uint8_t *_in, size_t in_frames,
+ uint8_t *_out)
+{
+ size_t i;
+ const int16_t *in = (const int16_t *)_in;
+ int16_t *out = (int16_t *)_out;
+
+ memset(out, 0, sizeof(*out) * 6 * in_frames);
+
+ if (font_left != -1 && front_right != -1 && rear_left != -1 &&
+ rear_right != -1)
+ for (i = 0; i < in_frames; i++) {
+ out[6 * i + font_left] = in[4 * i];
+ out[6 * i + front_right] = in[4 * i + 1];
+ out[6 * i + rear_left] = in[4 * i + 2];
+ out[6 * i + rear_right] = in[4 * i + 3];
+ }
+ else
+ /* Use default 5.1 channel mapping for the conversion.
+ */
+ for (i = 0; i < in_frames; i++) {
+ out[6 * i] = in[4 * i];
+ out[6 * i + 1] = in[4 * i + 1];
+ out[6 * i + 4] = in[4 * i + 2];
+ out[6 * i + 5] = in[4 * i + 3];
+ }
+
+ return in_frames;
+}
+
+/*
* Channel converter: 5.1 surround to stereo.
*
* The out buffer can have room for just stereo samples. This convert function
diff --git a/cras/src/server/cras_fmt_conv_ops.h b/cras/src/server/cras_fmt_conv_ops.h
index a1a57487..0af7564b 100644
--- a/cras/src/server/cras_fmt_conv_ops.h
+++ b/cras/src/server/cras_fmt_conv_ops.h
@@ -46,6 +46,13 @@ size_t s16_stereo_to_51(size_t left, size_t right, size_t center,
const uint8_t *in, size_t in_frames, uint8_t *out);
/*
+ * Channel converter: quad to 5.1 surround.
+ */
+size_t s16_quad_to_51(size_t font_left, size_t front_right, size_t rear_left,
+ size_t rear_right, const uint8_t *in, size_t in_frames,
+ uint8_t *out);
+
+/*
* Channel converter: 5.1 surround to stereo.
*/
size_t s16_51_to_stereo(const uint8_t *in, size_t in_frames, uint8_t *out);
diff --git a/cras/src/server/cras_hfp_ag_profile.c b/cras/src/server/cras_hfp_ag_profile.c
index 9d59d40e..b5fcecc3 100644
--- a/cras/src/server/cras_hfp_ag_profile.c
+++ b/cras/src/server/cras_hfp_ag_profile.c
@@ -20,7 +20,6 @@
#include "cras_server_metrics.h"
#include "cras_system_state.h"
#include "cras_iodev_list.h"
-#include "cras_observer.h"
#include "utlist.h"
#include "packet_status_logger.h"
@@ -461,19 +460,6 @@ struct packet_status_logger *cras_hfp_ag_get_wbs_logger()
return &wbs_logger;
}
-void cras_hfp_ag_resend_device_battery_level()
-{
- struct audio_gateway *ag;
- int level;
- DL_FOREACH (connected_ags, ag) {
- level = hfp_slc_get_hf_battery_level(ag->slc_handle);
- if (level >= 0 && level <= 100)
- cras_observer_notify_bt_battery_changed(
- cras_bt_device_address(ag->device),
- (uint32_t)(level));
- }
-}
-
int cras_hsp_ag_profile_create(DBusConnection *conn)
{
return cras_bt_add_profile(conn, &cras_hsp_ag_profile);
diff --git a/cras/src/server/cras_hfp_ag_profile.h b/cras/src/server/cras_hfp_ag_profile.h
index 50d27e05..3de56184 100644
--- a/cras/src/server/cras_hfp_ag_profile.h
+++ b/cras/src/server/cras_hfp_ag_profile.h
@@ -56,8 +56,4 @@ struct hfp_slc_handle *cras_hfp_ag_get_slc(struct cras_bt_device *device);
/* Gets the logger for WBS packet status. */
struct packet_status_logger *cras_hfp_ag_get_wbs_logger();
-/* Iterate all possible AGs (theoratically only one) and signal its battery
- * level */
-void cras_hfp_ag_resend_device_battery_level();
-
#endif /* CRAS_HFP_AG_PROFILE_H_ */
diff --git a/cras/src/server/cras_hfp_alsa_iodev.c b/cras/src/server/cras_hfp_alsa_iodev.c
index b80a88c7..c1b60b30 100644
--- a/cras/src/server/cras_hfp_alsa_iodev.c
+++ b/cras/src/server/cras_hfp_alsa_iodev.c
@@ -12,7 +12,6 @@
#include "cras_iodev.h"
#include "cras_system_state.h"
#include "cras_util.h"
-#include "sfh.h"
#include "utlist.h"
#include "cras_bt_device.h"
@@ -108,6 +107,7 @@ static int hfp_alsa_configure_dev(struct cras_iodev *iodev)
return rc;
}
+ hfp_set_call_status(hfp_alsa_io->slc, 1);
iodev->buffer_size = aio->buffer_size;
return 0;
@@ -118,6 +118,7 @@ static int hfp_alsa_close_dev(struct cras_iodev *iodev)
struct hfp_alsa_io *hfp_alsa_io = (struct hfp_alsa_io *)iodev;
struct cras_iodev *aio = hfp_alsa_io->aio;
+ hfp_set_call_status(hfp_alsa_io->slc, 0);
cras_bt_device_put_sco(hfp_alsa_io->device);
cras_iodev_free_format(iodev);
return aio->close_dev(aio);
@@ -259,10 +260,7 @@ struct cras_iodev *hfp_alsa_iodev_create(struct cras_iodev *aio,
name = cras_bt_device_object_path(device);
snprintf(iodev->info.name, sizeof(iodev->info.name), "%s", name);
iodev->info.name[ARRAY_SIZE(iodev->info.name) - 1] = 0;
- iodev->info.stable_id =
- SuperFastHash(cras_bt_device_object_path(device),
- strlen(cras_bt_device_object_path(device)),
- strlen(cras_bt_device_object_path(device)));
+ iodev->info.stable_id = cras_bt_device_get_stable_id(device);
iodev->open_dev = hfp_alsa_open_dev;
iodev->update_supported_formats = hfp_alsa_update_supported_formats;
diff --git a/cras/src/server/cras_hfp_iodev.c b/cras/src/server/cras_hfp_iodev.c
index 7cce3736..6a4ced04 100644
--- a/cras/src/server/cras_hfp_iodev.c
+++ b/cras/src/server/cras_hfp_iodev.c
@@ -17,7 +17,6 @@
#include "cras_iodev.h"
#include "cras_system_state.h"
#include "cras_util.h"
-#include "sfh.h"
#include "utlist.h"
/* Implementation of bluetooth hands-free profile iodev.
@@ -167,6 +166,7 @@ static int configure_dev(struct cras_iodev *iodev)
hfpio->filled_zeros = 0;
add_dev:
hfp_info_add_iodev(hfpio->info, iodev->direction, iodev->format);
+ hfp_set_call_status(hfpio->slc, 1);
iodev->buffer_size = hfp_buf_size(hfpio->info, iodev->direction);
@@ -181,8 +181,10 @@ static int close_dev(struct cras_iodev *iodev)
struct hfp_io *hfpio = (struct hfp_io *)iodev;
hfp_info_rm_iodev(hfpio->info, iodev->direction);
- if (hfp_info_running(hfpio->info) && !hfp_info_has_iodev(hfpio->info))
+ if (hfp_info_running(hfpio->info) && !hfp_info_has_iodev(hfpio->info)) {
hfp_info_stop(hfpio->info);
+ hfp_set_call_status(hfpio->slc, 0);
+ }
cras_iodev_free_format(iodev);
cras_iodev_free_audio_area(iodev);
@@ -306,10 +308,7 @@ struct cras_iodev *hfp_iodev_create(enum CRAS_STREAM_DIRECTION dir,
snprintf(iodev->info.name, sizeof(iodev->info.name), "%s", name);
iodev->info.name[ARRAY_SIZE(iodev->info.name) - 1] = 0;
- iodev->info.stable_id =
- SuperFastHash(cras_bt_device_object_path(device),
- strlen(cras_bt_device_object_path(device)),
- strlen(cras_bt_device_object_path(device)));
+ iodev->info.stable_id = cras_bt_device_get_stable_id(device);
iodev->configure_dev = configure_dev;
iodev->frames_queued = frames_queued;
diff --git a/cras/src/server/cras_hfp_slc.c b/cras/src/server/cras_hfp_slc.c
index e4f0127d..28f73edc 100644
--- a/cras/src/server/cras_hfp_slc.c
+++ b/cras/src/server/cras_hfp_slc.c
@@ -441,12 +441,10 @@ static int available_codecs(struct hfp_slc_handle *handle, const char *cmd)
id_str = strtok(NULL, ",");
}
- for (id = HFP_MAX_CODECS - 1; id > 0; id--) {
- if (handle->hf_codec_supported[id]) {
- handle->preferred_codec = id;
- break;
- }
- }
+ if (hfp_slc_get_wideband_speech_supported(handle))
+ handle->preferred_codec = HFP_CODEC_ID_MSBC;
+ else
+ handle->preferred_codec = HFP_CODEC_ID_CVSD;
free(tokens);
return hfp_send(handle, AT_CMD("OK"));
@@ -609,6 +607,26 @@ static int operator_selection(struct hfp_slc_handle *handle, const char *buf)
return hfp_send(handle, AT_CMD("OK"));
}
+/* The AT+CHLD command is used to control call hold, release, and multiparty
+ * states.
+ */
+static int call_hold(struct hfp_slc_handle *handle, const char *buf)
+{
+ int rc;
+
+ // Chrome OS doesn't yet support CHLD features but we need to reply
+ // the query with an empty feature list rather than "ERROR" to increase
+ // interoperability with certain devices (b/172413440).
+ if (strlen(buf) > 8 && buf[7] == '=' && buf[8] == '?') {
+ rc = hfp_send(handle, AT_CMD("+CHLD:"));
+ if (rc)
+ return rc;
+ return hfp_send(handle, AT_CMD("OK"));
+ }
+
+ return hfp_send(handle, AT_CMD("ERROR"));
+}
+
/* AT+CIND command retrieves the supported indicator and its corresponding
* range and order index or read current status of indicators. Mandatory
* support per spec 4.2.
@@ -938,6 +956,70 @@ static int terminate_call(struct hfp_slc_handle *handle, const char *cmd)
return cras_telephony_event_terminate_call();
}
+/* AT+XEVENT is defined by Android to support vendor specific features.
+ * Currently, the only known supported case for CrOS is the battery event sent
+ * by some Plantronics headsets.
+ */
+static int vendor_specific_features(struct hfp_slc_handle *handle,
+ const char *cmd)
+{
+ char *tokens, *event, *level_str, *num_of_level_str;
+ int level, num_of_level;
+
+ tokens = strdup(cmd);
+ strtok(tokens, "=");
+ event = strtok(NULL, ",");
+ if (!event)
+ goto error_out;
+
+ /* AT+XEVENT=BATTERY,Level,NumberOfLevel,MinutesOfTalkTime,IsCharging
+ * Level: The charge level with a zero-based integer.
+ * NumberOfLevel: How many charging levels there are.
+ * MinuteOfTalkTime: The estimated number of talk minutes remaining.
+ * IsCharging: A 0 or 1 value.
+ *
+ * We only support the battery level and thus only care about the first
+ * 3 arguments.
+ */
+ if (!strncmp(event, "BATTERY", 7)) {
+ level_str = strtok(NULL, ",");
+ num_of_level_str = strtok(NULL, ",");
+ if (!level_str || !num_of_level_str)
+ goto error_out;
+
+ level = atoi(level_str);
+ num_of_level = atoi(num_of_level_str);
+ if (level < 0 || num_of_level <= 1 || level >= num_of_level)
+ goto error_out;
+
+ level = (int64_t)level * 100 / (num_of_level - 1);
+ if (handle->hf_battery != level) {
+ handle->hf_supports_battery_indicator |=
+ CRAS_HFP_BATTERY_INDICATOR_PLANTRONICS;
+ cras_server_metrics_hfp_battery_report(
+ CRAS_HFP_BATTERY_INDICATOR_PLANTRONICS);
+ handle->hf_battery = level;
+ cras_observer_notify_bt_battery_changed(
+ cras_bt_device_address(handle->device),
+ (uint32_t)(level));
+ }
+ }
+
+ free(tokens);
+ /* For Plantronic headsets, it is required to reply "OK" for the first
+ * AT+XEVENT=USER-AGENT... command to tell the headset our support of
+ * the xevent protocol. Otherwise, all following events including
+ * BATTERY won't be sent.
+ */
+ return hfp_send(handle, AT_CMD("OK"));
+
+error_out:
+ syslog(LOG_ERR, "%s: malformed vendor specific command: '%s'", __func__,
+ cmd);
+ free(tokens);
+ return hfp_send(handle, AT_CMD("ERROR"));
+}
+
/* AT commands to support in order to conform HFP specification.
*
* An initialized service level connection is the pre-condition for all
@@ -999,6 +1081,8 @@ static struct at_command at_commands[] = {
{ "AT+VG", signal_gain_setting },
{ "AT+VTS", dtmf_tone },
{ "AT+XAPL", apple_supported_features },
+ { "AT+XEVENT", vendor_specific_features },
+ { "AT+CHLD", call_hold },
{ 0 }
};
@@ -1314,8 +1398,3 @@ int hfp_slc_get_hf_supports_battery_indicator(struct hfp_slc_handle *handle)
{
return handle->hf_supports_battery_indicator;
}
-
-int hfp_slc_get_hf_battery_level(struct hfp_slc_handle *handle)
-{
- return handle->hf_battery;
-}
diff --git a/cras/src/server/cras_hfp_slc.h b/cras/src/server/cras_hfp_slc.h
index c3cdc117..99335eab 100644
--- a/cras/src/server/cras_hfp_slc.h
+++ b/cras/src/server/cras_hfp_slc.h
@@ -62,6 +62,7 @@ struct cras_bt_device;
#define CRAS_HFP_BATTERY_INDICATOR_NONE 0x0
#define CRAS_HFP_BATTERY_INDICATOR_HFP 0x1
#define CRAS_HFP_BATTERY_INDICATOR_APPLE 0x2
+#define CRAS_HFP_BATTERY_INDICATOR_PLANTRONICS 0x4
/* Callback to call when service level connection initialized. */
typedef int (*hfp_slc_init_cb)(struct hfp_slc_handle *handle);
@@ -145,10 +146,6 @@ int hfp_slc_get_ag_codec_negotiation_supported(struct hfp_slc_handle *handle);
* Apple, HFP, none, or both. */
int hfp_slc_get_hf_supports_battery_indicator(struct hfp_slc_handle *handle);
-/* Gets the battery level for the HF. The data ranges 0 ~ 100. Use -1 for no
- * battery level reported.*/
-int hfp_slc_get_hf_battery_level(struct hfp_slc_handle *handle);
-
/* Init the codec negotiation process if needed. */
int hfp_slc_codec_connection_setup(struct hfp_slc_handle *handle);
diff --git a/cras/src/server/cras_iodev.c b/cras/src/server/cras_iodev.c
index fd1ce805..651cef71 100644
--- a/cras/src/server/cras_iodev.c
+++ b/cras/src/server/cras_iodev.c
@@ -732,6 +732,17 @@ bool cras_iodev_is_aec_use_case(const struct cras_ionode *node)
return false;
}
+bool cras_iodev_is_on_internal_card(const struct cras_ionode *node)
+{
+ if (node->type == CRAS_NODE_TYPE_INTERNAL_SPEAKER)
+ return true;
+ if (node->type == CRAS_NODE_TYPE_HEADPHONE)
+ return true;
+ if (node->type == CRAS_NODE_TYPE_MIC)
+ return true;
+ return false;
+}
+
float cras_iodev_get_software_volume_scaler(struct cras_iodev *iodev)
{
unsigned int volume;
@@ -1010,6 +1021,7 @@ int cras_iodev_close(struct cras_iodev *iodev)
if (iodev->active_node) {
cras_server_metrics_device_runtime(iodev);
+ cras_server_metrics_device_gain(iodev);
cras_server_metrics_device_volume(iodev);
}
@@ -1695,3 +1707,13 @@ int cras_iodev_drop_frames_by_time(struct cras_iodev *iodev, struct timespec ts)
return rc;
}
+
+bool cras_iodev_support_noise_cancellation(const struct cras_iodev *iodev)
+{
+ if (iodev->direction != CRAS_STREAM_INPUT)
+ return false;
+
+ if (iodev->support_noise_cancellation)
+ return !!iodev->support_noise_cancellation(iodev);
+ return false;
+}
diff --git a/cras/src/server/cras_iodev.h b/cras/src/server/cras_iodev.h
index db16a0f8..18a0962c 100644
--- a/cras/src/server/cras_iodev.h
+++ b/cras/src/server/cras_iodev.h
@@ -184,6 +184,8 @@ struct cras_ionode {
* audio thread can sleep before serving this playback dev the next time.
* Not implementing this ops means fall back to default behavior in
* cras_iodev_default_frames_to_play_in_sleep().
+ * support_noise_cancellation - (Optional) Checks if the device supports noise
+ * cancellation.
* format - The audio format being rendered or captured to hardware.
* rate_est - Rate estimator to estimate the actual device rate.
* area - Information about how the samples are stored.
@@ -274,6 +276,7 @@ struct cras_iodev {
unsigned int (*frames_to_play_in_sleep)(struct cras_iodev *iodev,
unsigned int *hw_level,
struct timespec *hw_tstamp);
+ int (*support_noise_cancellation)(const struct cras_iodev *iodev);
struct cras_audio_format *format;
struct rate_estimator *rate_est;
struct cras_audio_area *area;
@@ -459,6 +462,9 @@ void cras_iodev_set_active_node(struct cras_iodev *iodev,
/* Checks if the node is the typical playback or capture option for AEC usage. */
bool cras_iodev_is_aec_use_case(const struct cras_ionode *node);
+/* Checks if the node is a playback or capture node on internal card. */
+bool cras_iodev_is_on_internal_card(const struct cras_ionode *node);
+
/* Adjust the system volume based on the volume of the given node. */
static inline unsigned int
cras_iodev_adjust_node_volume(const struct cras_ionode *node,
@@ -833,4 +839,12 @@ void cras_iodev_update_highest_hw_level(struct cras_iodev *iodev,
int cras_iodev_drop_frames_by_time(struct cras_iodev *iodev,
struct timespec ts);
+/* Checks if an input device supports noise cancellation.
+ * Args:
+ * iodev - The device.
+ * Returns:
+ * True if device supports noise cancellation. False otherwise.
+ */
+bool cras_iodev_support_noise_cancellation(const struct cras_iodev *iodev);
+
#endif /* CRAS_IODEV_H_ */
diff --git a/cras/src/server/cras_iodev_list.c b/cras/src/server/cras_iodev_list.c
index ada29719..b818c97b 100644
--- a/cras/src/server/cras_iodev_list.c
+++ b/cras/src/server/cras_iodev_list.c
@@ -91,6 +91,9 @@ static int stream_list_suspended = 0;
static const unsigned int INIT_DEV_DELAY_MS = 1000;
/* Flag to indicate that hotword streams are suspended. */
static int hotword_suspended = 0;
+/* Flag to indicate that suspended hotword streams should be auto-resumed at
+ * system resume. */
+static int hotword_auto_resume = 0;
static void idle_dev_check(struct cras_timer *timer, void *data);
@@ -388,8 +391,9 @@ static void close_dev(struct cras_iodev *dev)
MAINLOG(main_log, MAIN_THREAD_DEV_CLOSE, dev->info.idx, 0, 0);
remove_all_streams_from_dev(dev);
dev->idle_timeout.tv_sec = 0;
- cras_iodev_close(dev);
+ /* close echo ref first to avoid underrun in hardware */
possibly_disable_echo_reference(dev);
+ cras_iodev_close(dev);
}
static void idle_dev_check(struct cras_timer *timer, void *data)
@@ -490,6 +494,11 @@ static void suspend_devs()
if (rstream->is_pinned) {
struct cras_iodev *dev;
+ /* Skip closing hotword stream in the first pass.
+ * Closing an input device may resume hotword stream
+ * with its post_close_iodev_hook so we should deal
+ * with hotword stream in the second pass.
+ */
if ((rstream->flags & HOTWORD_STREAM) == HOTWORD_STREAM)
continue;
@@ -513,6 +522,14 @@ static void suspend_devs()
DL_FOREACH (enabled_devs[CRAS_STREAM_INPUT], edev) {
close_dev(edev->dev);
}
+
+ /* Doing this check after all the other enabled iodevs are closed to
+ * ensure preempted hotword streams obey the pause_at_suspend flag.
+ */
+ if (cras_system_get_hotword_pause_at_suspend()) {
+ cras_iodev_list_suspend_hotword_streams();
+ hotword_auto_resume = 1;
+ }
}
static int stream_added_cb(struct cras_rstream *rstream);
@@ -527,6 +544,14 @@ static void resume_devs()
MAINLOG(main_log, MAIN_THREAD_RESUME_DEVS, 0, 0, 0);
+ /* Auto-resume based on the local flag in case the system state flag has
+ * changed.
+ */
+ if (hotword_auto_resume) {
+ cras_iodev_list_resume_hotword_stream();
+ hotword_auto_resume = 0;
+ }
+
/*
* To remove the short popped noise caused by applications that can not
* stop playback "right away" after resume, we mute all output devices
@@ -1856,6 +1881,24 @@ void cras_iodev_list_unregister_loopback(enum CRAS_LOOPBACK_TYPE type,
}
}
+void cras_iodev_list_reset_for_noise_cancellation()
+{
+ struct cras_iodev *dev;
+ bool enabled = cras_system_get_noise_cancellation_enabled();
+
+ DL_FOREACH (devs[CRAS_STREAM_INPUT].iodevs, dev) {
+ if (!cras_iodev_is_open(dev) ||
+ !cras_iodev_support_noise_cancellation(dev))
+ continue;
+ syslog(LOG_INFO, "Re-open %s for %s noise cancellation",
+ dev->info.name, enabled ? "enabling" : "disabling");
+ possibly_enable_fallback(CRAS_STREAM_INPUT, false);
+ cras_iodev_list_suspend_dev(dev->info.idx);
+ cras_iodev_list_resume_dev(dev->info.idx);
+ possibly_disable_fallback(CRAS_STREAM_INPUT);
+ }
+}
+
void cras_iodev_list_reset()
{
struct enabled_dev *edev;
diff --git a/cras/src/server/cras_iodev_list.h b/cras/src/server/cras_iodev_list.h
index 61c3a182..d6e9ba54 100644
--- a/cras/src/server/cras_iodev_list.h
+++ b/cras/src/server/cras_iodev_list.h
@@ -274,6 +274,11 @@ int cras_iodev_list_suspend_hotword_streams();
/* Resumes all hotwording streams. */
int cras_iodev_list_resume_hotword_stream();
+/* Sets the state of noise cancellation for input devices which supports noise
+ * cancellation by suspend, enable/disable, then resume.
+ */
+void cras_iodev_list_reset_for_noise_cancellation();
+
/* For unit test only. */
void cras_iodev_list_reset();
diff --git a/cras/src/server/cras_rclient_util.c b/cras/src/server/cras_rclient_util.c
index def645e3..0af98863 100644
--- a/cras/src/server/cras_rclient_util.c
+++ b/cras/src/server/cras_rclient_util.c
@@ -170,6 +170,8 @@ int rclient_handle_client_stream_connect(struct cras_rclient *client,
if (rc)
goto cleanup_config;
+ detect_rtc_stream_pair(cras_iodev_list_get_stream_list(), stream);
+
/* Tell client about the stream setup. */
syslog(LOG_DEBUG, "Send connected for stream %x\n", msg->stream_id);
diff --git a/cras/src/server/cras_rstream.c b/cras/src/server/cras_rstream.c
index 94adcead..3c0a0ce3 100644
--- a/cras/src/server/cras_rstream.c
+++ b/cras/src/server/cras_rstream.c
@@ -167,6 +167,11 @@ static int verify_rstream_parameters(const struct cras_rstream_config *config,
syslog(LOG_ERR, "rstream: Invalid stream type.\n");
return -EINVAL;
}
+ if (config->client_type < CRAS_CLIENT_TYPE_UNKNOWN ||
+ config->client_type >= CRAS_NUM_CLIENT_TYPE) {
+ syslog(LOG_ERR, "rstream: Invalid client type.\n");
+ return -EINVAL;
+ }
if ((config->client_shm_size > 0 && config->client_shm_fd < 0) ||
(config->client_shm_size == 0 && config->client_shm_fd >= 0)) {
syslog(LOG_ERR, "rstream: invalid client-provided shm info\n");
@@ -287,8 +292,8 @@ int cras_rstream_create(struct cras_rstream_config *config,
stream->cb_threshold = config->cb_threshold;
stream->client = config->client;
stream->shm = NULL;
- stream->master_dev.dev_id = NO_DEVICE;
- stream->master_dev.dev_ptr = NULL;
+ stream->main_dev.dev_id = NO_DEVICE;
+ stream->main_dev.dev_ptr = NULL;
stream->num_missed_cb = 0;
stream->is_pinned = (config->dev_idx != NO_DEVICE);
stream->pinned_dev_idx = config->dev_idx;
@@ -426,12 +431,12 @@ void cras_rstream_dev_attach(struct cras_rstream *rstream, unsigned int dev_id,
if (buffer_share_add_id(rstream->buf_state, dev_id, dev_ptr) == 0)
rstream->num_attached_devs++;
- /* TODO(hychao): Handle master device assignment for complicated
+ /* TODO(hychao): Handle main device assignment for complicated
* routing case.
*/
- if (rstream->master_dev.dev_id == NO_DEVICE) {
- rstream->master_dev.dev_id = dev_id;
- rstream->master_dev.dev_ptr = dev_ptr;
+ if (rstream->main_dev.dev_id == NO_DEVICE) {
+ rstream->main_dev.dev_id = dev_id;
+ rstream->main_dev.dev_ptr = dev_ptr;
}
}
@@ -440,18 +445,18 @@ void cras_rstream_dev_detach(struct cras_rstream *rstream, unsigned int dev_id)
if (buffer_share_rm_id(rstream->buf_state, dev_id) == 0)
rstream->num_attached_devs--;
- if (rstream->master_dev.dev_id == dev_id) {
+ if (rstream->main_dev.dev_id == dev_id) {
int i;
struct id_offset *o;
- /* Choose the first device id as master. */
- rstream->master_dev.dev_id = NO_DEVICE;
- rstream->master_dev.dev_ptr = NULL;
+ /* Choose the first device id as a main device. */
+ rstream->main_dev.dev_id = NO_DEVICE;
+ rstream->main_dev.dev_ptr = NULL;
for (i = 0; i < rstream->buf_state->id_sz; i++) {
o = &rstream->buf_state->wr_idx[i];
if (o->used) {
- rstream->master_dev.dev_id = o->id;
- rstream->master_dev.dev_ptr = o->data;
+ rstream->main_dev.dev_id = o->id;
+ rstream->main_dev.dev_ptr = o->data;
break;
}
}
diff --git a/cras/src/server/cras_rstream.h b/cras/src/server/cras_rstream.h
index 3bf7df0b..d57c13be 100644
--- a/cras/src/server/cras_rstream.h
+++ b/cras/src/server/cras_rstream.h
@@ -20,12 +20,12 @@ struct cras_connect_message;
struct cras_rclient;
struct dev_mix;
-/* Holds informations about the master active device.
+/* Holds informations about the main active device.
* Members:
- * dev_id - id of the master device.
- * dev_ptr - pointer to the master device.
+ * dev_id - id of the main device.
+ * dev_ptr - pointer to the main device.
*/
-struct master_dev_info {
+struct main_dev_info {
int dev_id;
void *dev_ptr;
};
@@ -42,7 +42,7 @@ struct master_dev_info {
* fd - Socket for requesting and sending audio buffer events.
* buffer_frames - Buffer size in frames.
* cb_threshold - Callback client when this much is left.
- * master_dev_info - The info of the master device this stream attaches to.
+ * main_dev_info - The info of the main device this stream attaches to.
* is_draining - The stream is draining and waiting to be removed.
* client - The client who uses this stream.
* shm - shared memory
@@ -74,7 +74,7 @@ struct cras_rstream {
size_t buffer_frames;
size_t cb_threshold;
int is_draining;
- struct master_dev_info master_dev;
+ struct main_dev_info main_dev;
struct cras_rclient *client;
struct cras_audio_shm *shm;
struct cras_audio_area *audio_area;
diff --git a/cras/src/server/cras_server_metrics.c b/cras/src/server/cras_server_metrics.c
index ef4011bd..7e487107 100644
--- a/cras/src/server/cras_server_metrics.c
+++ b/cras/src/server/cras_server_metrics.c
@@ -25,7 +25,9 @@ const char kBusyloop[] = "Cras.Busyloop";
const char kBusyloopLength[] = "Cras.BusyloopLength";
const char kDeviceTypeInput[] = "Cras.DeviceTypeInput";
const char kDeviceTypeOutput[] = "Cras.DeviceTypeOutput";
+const char kDeviceGain[] = "Cras.DeviceGain";
const char kDeviceVolume[] = "Cras.DeviceVolume";
+const char kFetchDelayMilliSeconds[] = "Cras.FetchDelayMilliSeconds";
const char kHighestDeviceDelayInput[] = "Cras.HighestDeviceDelayInput";
const char kHighestDeviceDelayOutput[] = "Cras.HighestDeviceDelayOutput";
const char kHighestInputHardwareLevel[] = "Cras.HighestInputHardwareLevel";
@@ -47,12 +49,12 @@ const char kMissedCallbackSecondTimeInput[] =
const char kMissedCallbackSecondTimeOutput[] =
"Cras.MissedCallbackSecondTimeOutput";
const char kNoCodecsFoundMetric[] = "Cras.NoCodecsFoundAtBoot";
-const char kStreamTimeoutMilliSeconds[] = "Cras.StreamTimeoutMilliSeconds";
const char kStreamCallbackThreshold[] = "Cras.StreamCallbackThreshold";
const char kStreamClientTypeInput[] = "Cras.StreamClientTypeInput";
const char kStreamClientTypeOutput[] = "Cras.StreamClientTypeOutput";
const char kStreamFlags[] = "Cras.StreamFlags";
const char kStreamEffects[] = "Cras.StreamEffects";
+const char kStreamRuntime[] = "Cras.StreamRuntime";
const char kStreamSamplingFormat[] = "Cras.StreamSamplingFormat";
const char kStreamSamplingRate[] = "Cras.StreamSamplingRate";
const char kUnderrunsPerDevice[] = "Cras.UnderrunsPerDevice";
@@ -93,6 +95,7 @@ enum CRAS_SERVER_METRICS_TYPE {
BT_WIDEBAND_SELECTED_CODEC,
BUSYLOOP,
BUSYLOOP_LENGTH,
+ DEVICE_GAIN,
DEVICE_RUNTIME,
DEVICE_VOLUME,
HIGHEST_DEVICE_DELAY_INPUT,
@@ -163,7 +166,8 @@ struct cras_server_metrics_device_data {
};
struct cras_server_metrics_stream_data {
- enum CRAS_CLIENT_TYPE type;
+ enum CRAS_CLIENT_TYPE client_type;
+ enum CRAS_STREAM_TYPE stream_type;
enum CRAS_STREAM_DIRECTION direction;
struct timespec runtime;
};
@@ -304,6 +308,31 @@ metrics_client_type_str(enum CRAS_CLIENT_TYPE client_type)
return "ServerStream";
case CRAS_CLIENT_TYPE_LACROS:
return "LaCrOS";
+ case CRAS_CLIENT_TYPE_PLUGIN:
+ return "PluginVM";
+ case CRAS_CLIENT_TYPE_ARCVM:
+ return "ARCVM";
+ default:
+ return "InvalidType";
+ }
+}
+
+static inline const char *
+metrics_stream_type_str(enum CRAS_STREAM_TYPE stream_type)
+{
+ switch (stream_type) {
+ case CRAS_STREAM_TYPE_DEFAULT:
+ return "Default";
+ case CRAS_STREAM_TYPE_MULTIMEDIA:
+ return "Multimedia";
+ case CRAS_STREAM_TYPE_VOICE_COMMUNICATION:
+ return "VoiceCommunication";
+ case CRAS_STREAM_TYPE_SPEECH_RECOGNITION:
+ return "SpeechRecognition";
+ case CRAS_STREAM_TYPE_PRO_AUDIO:
+ return "ProAudio";
+ case CRAS_STREAM_TYPE_ACCESSIBILITY:
+ return "Accessibility";
default:
return "InvalidType";
}
@@ -394,6 +423,69 @@ get_metrics_device_type(struct cras_iodev *iodev)
}
}
+/*
+ * Logs metrics for each group it belongs to. The UMA does not merge subgroups
+ * automatically so we need to log them separately.
+ *
+ * For example, if we call this function with argument (3, 48000,
+ * Cras.StreamSamplingRate, Input, Chrome), it will send 48000 to below
+ * metrics:
+ * Cras.StreamSamplingRate.Input.Chrome
+ * Cras.StreamSamplingRate.Input
+ * Cras.StreamSamplingRate
+ */
+static void log_sparse_histogram_each_level(int num, int sample, ...)
+{
+ char metrics_name[METRICS_NAME_BUFFER_SIZE] = {};
+ va_list valist;
+ int i, len = 0;
+
+ va_start(valist, sample);
+
+ for (i = 0; i < num && len < METRICS_NAME_BUFFER_SIZE; i++) {
+ int metric_len =
+ snprintf(metrics_name + len,
+ METRICS_NAME_BUFFER_SIZE - len, "%s%s",
+ i ? "." : "", va_arg(valist, char *));
+ // Exit early on error or running out of bufferspace. Avoids
+ // logging partial or corrupted strings.
+ if (metric_len < 0 ||
+ metric_len > METRICS_NAME_BUFFER_SIZE - len)
+ break;
+ len += metric_len;
+ cras_metrics_log_sparse_histogram(metrics_name, sample);
+ }
+
+ va_end(valist);
+}
+
+static void log_histogram_each_level(int num, int sample, int min, int max,
+ int nbuckets, ...)
+{
+ char metrics_name[METRICS_NAME_BUFFER_SIZE] = {};
+ va_list valist;
+ int i, len = 0;
+
+ va_start(valist, nbuckets);
+
+ for (i = 0; i < num && len < METRICS_NAME_BUFFER_SIZE; i++) {
+ int metric_len =
+ snprintf(metrics_name + len,
+ METRICS_NAME_BUFFER_SIZE - len, "%s%s",
+ i ? "." : "", va_arg(valist, char *));
+ // Exit early on error or running out of bufferspace. Avoids
+ // logging partial or corrupted strings.
+ if (metric_len < 0 ||
+ metric_len > METRICS_NAME_BUFFER_SIZE - len)
+ break;
+ len += metric_len;
+ cras_metrics_log_histogram(metrics_name, sample, min, max,
+ nbuckets);
+ }
+
+ va_end(valist);
+}
+
int cras_server_metrics_hfp_sco_connection_error(
enum CRAS_METRICS_BT_SCO_ERROR_TYPE type)
{
@@ -536,6 +628,31 @@ int cras_server_metrics_device_runtime(struct cras_iodev *iodev)
return 0;
}
+int cras_server_metrics_device_gain(struct cras_iodev *iodev)
+{
+ struct cras_server_metrics_message msg;
+ union cras_server_metrics_data data;
+ int err;
+
+ if (iodev->direction == CRAS_STREAM_OUTPUT)
+ return 0;
+
+ data.device_data.type = get_metrics_device_type(iodev);
+ data.device_data.value =
+ (unsigned)100 * iodev->active_node->ui_gain_scaler;
+
+ init_server_metrics_msg(&msg, DEVICE_GAIN, data);
+
+ err = cras_server_metrics_message_send(
+ (struct cras_main_message *)&msg);
+ if (err < 0) {
+ syslog(LOG_ERR, "Failed to send metrics message: DEVICE_GAIN");
+ return err;
+ }
+
+ return 0;
+}
+
int cras_server_metrics_device_volume(struct cras_iodev *iodev)
{
struct cras_server_metrics_message msg;
@@ -640,13 +757,31 @@ int cras_server_metrics_highest_hw_level(unsigned hw_level,
return 0;
}
-int cras_server_metrics_longest_fetch_delay(unsigned delay_msec)
+/* Logs longest fetch delay of a stream. */
+int cras_server_metrics_longest_fetch_delay(const struct cras_rstream *stream)
{
struct cras_server_metrics_message msg;
union cras_server_metrics_data data;
int err;
- data.value = delay_msec;
+ data.stream_data.client_type = stream->client_type;
+ data.stream_data.stream_type = stream->stream_type;
+ data.stream_data.direction = stream->direction;
+
+ /*
+ * There is no delay when the sleep_interval_ts larger than the
+ * longest_fetch_interval.
+ */
+ if (!timespec_after(&stream->longest_fetch_interval,
+ &stream->sleep_interval_ts)) {
+ data.stream_data.runtime.tv_sec = 0;
+ data.stream_data.runtime.tv_nsec = 0;
+ } else {
+ subtract_timespecs(&stream->longest_fetch_interval,
+ &stream->sleep_interval_ts,
+ &data.stream_data.runtime);
+ }
+
init_server_metrics_msg(&msg, LONGEST_FETCH_DELAY, data);
err = cras_server_metrics_message_send(
(struct cras_main_message *)&msg);
@@ -869,7 +1004,8 @@ int cras_server_metrics_stream_runtime(const struct cras_rstream *stream)
struct timespec now;
int err;
- data.stream_data.type = stream->client_type;
+ data.stream_data.client_type = stream->client_type;
+ data.stream_data.stream_type = stream->stream_type;
data.stream_data.direction = stream->direction;
clock_gettime(CLOCK_MONOTONIC_RAW, &now);
subtract_timespecs(&now, &stream->start_ts, &data.stream_data.runtime);
@@ -899,7 +1035,9 @@ int cras_server_metrics_stream_destroy(const struct cras_rstream *stream)
if (rc < 0)
return rc;
rc = cras_server_metrics_stream_runtime(stream);
- return rc;
+ if (rc < 0)
+ return rc;
+ return cras_server_metrics_longest_fetch_delay(stream);
}
int cras_server_metrics_busyloop(struct timespec *ts, unsigned count)
@@ -960,6 +1098,15 @@ static void metrics_device_runtime(struct cras_server_metrics_device_data data)
cras_metrics_log_sparse_histogram(kDeviceTypeOutput, data.type);
}
+static void metrics_device_gain(struct cras_server_metrics_device_data data)
+{
+ char metrics_name[METRICS_NAME_BUFFER_SIZE];
+
+ snprintf(metrics_name, METRICS_NAME_BUFFER_SIZE, "%s.%s", kDeviceGain,
+ metrics_device_type_str(data.type));
+ cras_metrics_log_histogram(metrics_name, data.value, 0, 2000, 20);
+}
+
static void metrics_device_volume(struct cras_server_metrics_device_data data)
{
char metrics_name[METRICS_NAME_BUFFER_SIZE];
@@ -969,21 +1116,24 @@ static void metrics_device_volume(struct cras_server_metrics_device_data data)
cras_metrics_log_histogram(metrics_name, data.value, 0, 100, 20);
}
-static void metrics_stream_runtime(struct cras_server_metrics_stream_data data)
+static void
+metrics_longest_fetch_delay(struct cras_server_metrics_stream_data data)
{
- char metrics_name[METRICS_NAME_BUFFER_SIZE];
-
- snprintf(metrics_name, METRICS_NAME_BUFFER_SIZE, "Cras.%sStreamRuntime",
- data.direction == CRAS_STREAM_INPUT ? "Input" : "Output");
- cras_metrics_log_histogram(metrics_name, (unsigned)data.runtime.tv_sec,
- 0, 10000, 20);
+ int fetch_delay_msec =
+ data.runtime.tv_sec * 1000 + data.runtime.tv_nsec / 1000000;
+ log_histogram_each_level(3, fetch_delay_msec, 0, 10000, 20,
+ kFetchDelayMilliSeconds,
+ metrics_client_type_str(data.client_type),
+ metrics_stream_type_str(data.stream_type));
+}
- snprintf(metrics_name, METRICS_NAME_BUFFER_SIZE,
- "Cras.%sStreamRuntime.%s",
- data.direction == CRAS_STREAM_INPUT ? "Input" : "Output",
- metrics_client_type_str(data.type));
- cras_metrics_log_histogram(metrics_name, (unsigned)data.runtime.tv_sec,
- 0, 10000, 20);
+static void metrics_stream_runtime(struct cras_server_metrics_stream_data data)
+{
+ log_histogram_each_level(
+ 4, (int)data.runtime.tv_sec, 0, 10000, 20, kStreamRuntime,
+ data.direction == CRAS_STREAM_INPUT ? "Input" : "Output",
+ metrics_client_type_str(data.client_type),
+ metrics_stream_type_str(data.stream_type));
}
static void metrics_busyloop(struct cras_server_metrics_timespec_data data)
@@ -996,40 +1146,6 @@ static void metrics_busyloop(struct cras_server_metrics_timespec_data data)
cras_metrics_log_histogram(metrics_name, data.count, 0, 1000, 20);
}
-/*
- * Logs metrics for each group it belongs to. The UMA does not merge subgroups
- * automatically so we need to log them separately.
- *
- * For example, if we call this function with argument (3, 48000,
- * Cras.StreamSamplingRate, Input, Chrome), it will send 48000 to below
- * metrics:
- * Cras.StreamSamplingRate.Input.Chrome
- * Cras.StreamSamplingRate.Input
- * Cras.StreamSamplingRate
- */
-static void log_sparse_histogram_each_level(int num, int sample, ...)
-{
- char metrics_name[METRICS_NAME_BUFFER_SIZE] = {};
- va_list valist;
- int i, len = 0;
-
- va_start(valist, sample);
-
- for (i = 0; i < num && len < METRICS_NAME_BUFFER_SIZE; i++) {
- int metric_len = snprintf(metrics_name + len,
- METRICS_NAME_BUFFER_SIZE - len, "%s%s",
- i ? "." : "", va_arg(valist, char *));
- // Exit early on error or running out of bufferspace. Avoids
- // logging partial or corrupted strings.
- if (metric_len < 0 || metric_len > METRICS_NAME_BUFFER_SIZE - len)
- break;
- len += metric_len;
- cras_metrics_log_sparse_histogram(metrics_name, sample);
- }
-
- va_end(valist);
-}
-
static void
metrics_stream_config(struct cras_server_metrics_stream_config config)
{
@@ -1105,6 +1221,9 @@ static void handle_metrics_message(struct cras_main_message *msg, void *arg)
kHfpWidebandSpeechSelectedCodec,
metrics_msg->data.value);
break;
+ case DEVICE_GAIN:
+ metrics_device_gain(metrics_msg->data.device_data);
+ break;
case DEVICE_RUNTIME:
metrics_device_runtime(metrics_msg->data.device_data);
break;
@@ -1132,9 +1251,7 @@ static void handle_metrics_message(struct cras_main_message *msg, void *arg)
20);
break;
case LONGEST_FETCH_DELAY:
- cras_metrics_log_histogram(kStreamTimeoutMilliSeconds,
- metrics_msg->data.value, 1, 20000,
- 10);
+ metrics_longest_fetch_delay(metrics_msg->data.stream_data);
break;
case MISSED_CB_FIRST_TIME_INPUT:
cras_metrics_log_histogram(kMissedCallbackFirstTimeInput,
diff --git a/cras/src/server/cras_server_metrics.h b/cras/src/server/cras_server_metrics.h
index 91f13c3c..e8458087 100644
--- a/cras/src/server/cras_server_metrics.h
+++ b/cras/src/server/cras_server_metrics.h
@@ -49,6 +49,9 @@ int cras_server_metrics_hfp_packet_loss(float packet_loss_ratio);
/* Logs runtime of a device. */
int cras_server_metrics_device_runtime(struct cras_iodev *iodev);
+/* Logs the gain of a device. */
+int cras_server_metrics_device_gain(struct cras_iodev *iodev);
+
/* Logs the volume of a device. */
int cras_server_metrics_device_volume(struct cras_iodev *iodev);
@@ -61,9 +64,6 @@ int cras_server_metrics_highest_device_delay(
int cras_server_metrics_highest_hw_level(unsigned hw_level,
enum CRAS_STREAM_DIRECTION direction);
-/* Logs the longest fetch delay of a stream in millisecond. */
-int cras_server_metrics_longest_fetch_delay(unsigned delay_msec);
-
/* Logs the number of underruns of a device. */
int cras_server_metrics_num_underruns(unsigned num_underruns);
diff --git a/cras/src/server/cras_system_state.c b/cras/src/server/cras_system_state.c
index 331ecb11..366afb5f 100644
--- a/cras/src/server/cras_system_state.c
+++ b/cras/src/server/cras_system_state.c
@@ -14,9 +14,11 @@
#include <syslog.h>
#include "cras_alsa_card.h"
+#include "cras_alert.h"
#include "cras_board_config.h"
#include "cras_config.h"
#include "cras_device_blocklist.h"
+#include "cras_iodev_list.h"
#include "cras_observer.h"
#include "cras_shm.h"
#include "cras_system_state.h"
@@ -158,6 +160,9 @@ void cras_system_state_init(const char *device_config_dir, const char *shm_name,
exp_state->bt_wbs_enabled = board_config.bt_wbs_enabled;
exp_state->deprioritize_bt_wbs_mic =
board_config.deprioritize_bt_wbs_mic;
+ exp_state->noise_cancellation_enabled = 0;
+ exp_state->hotword_pause_at_suspend =
+ board_config.hotword_pause_at_suspend;
if ((rc = pthread_mutex_init(&state.update_lock, 0) != 0)) {
syslog(LOG_ERR, "Fatal: system state mutex init");
@@ -341,6 +346,7 @@ void cras_system_set_suspended(int suspended)
{
state.exp_state->suspended = suspended;
cras_observer_notify_suspend_changed(suspended);
+ cras_alert_process_all_pending_alerts();
}
void cras_system_set_volume_limits(long min, long max)
@@ -399,6 +405,20 @@ bool cras_system_get_bt_fix_a2dp_packet_size_enabled()
return state.bt_fix_a2dp_packet_size;
}
+void cras_system_set_noise_cancellation_enabled(bool enabled)
+{
+ /* When the flag is toggled, propagate to all iodevs immediately. */
+ if (cras_system_get_noise_cancellation_enabled() != enabled) {
+ state.exp_state->noise_cancellation_enabled = enabled;
+ cras_iodev_list_reset_for_noise_cancellation();
+ }
+}
+
+bool cras_system_get_noise_cancellation_enabled()
+{
+ return !!state.exp_state->noise_cancellation_enabled;
+}
+
bool cras_system_check_ignore_ucm_suffix(const char *card_name)
{
/* Check the general case: ALSA Loopback card "Loopback". */
@@ -414,6 +434,16 @@ bool cras_system_check_ignore_ucm_suffix(const char *card_name)
return false;
}
+bool cras_system_get_hotword_pause_at_suspend()
+{
+ return !!state.exp_state->hotword_pause_at_suspend;
+}
+
+void cras_system_set_hotword_pause_at_suspend(bool pause)
+{
+ state.exp_state->hotword_pause_at_suspend = pause;
+}
+
int cras_system_add_alsa_card(struct cras_alsa_card_info *alsa_card_info)
{
struct card_list *card;
diff --git a/cras/src/server/cras_system_state.h b/cras/src/server/cras_system_state.h
index ff04606a..bd09395c 100644
--- a/cras/src/server/cras_system_state.h
+++ b/cras/src/server/cras_system_state.h
@@ -134,9 +134,21 @@ void cras_system_set_bt_fix_a2dp_packet_size_enabled(bool enabled);
/* Gets the flag of Bluetooth fixed A2DP packet size. */
bool cras_system_get_bt_fix_a2dp_packet_size_enabled();
+/* Sets the flag to enable or disable Noise Cancellation. */
+void cras_system_set_noise_cancellation_enabled(bool enabled);
+
+/* Gets the flag of Noise Cancellation. */
+bool cras_system_get_noise_cancellation_enabled();
+
/* Checks if the card ignores the ucm suffix. */
bool cras_system_check_ignore_ucm_suffix(const char *card_name);
+/* Returns true if hotword detection is paused at system suspend. */
+bool cras_system_get_hotword_pause_at_suspend();
+
+/* Sets whether to pause hotword detection at system suspend. */
+void cras_system_set_hotword_pause_at_suspend(bool pause);
+
/* Adds a card at the given index to the system. When a new card is found
* (through a udev event notification) this will add the card to the system,
* causing its devices to become available for playback/capture.
diff --git a/cras/src/server/dev_io.c b/cras/src/server/dev_io.c
index 42fe9558..b311b221 100644
--- a/cras/src/server/dev_io.c
+++ b/cras/src/server/dev_io.c
@@ -10,6 +10,7 @@
#include "audio_thread_log.h"
#include "cras_audio_area.h"
#include "cras_audio_thread_monitor.h"
+#include "cras_device_monitor.h"
#include "cras_iodev.h"
#include "cras_non_empty_audio_handler.h"
#include "cras_rstream.h"
@@ -47,33 +48,78 @@ static const int DROP_FRAMES_THRESHOLD_MS = 50;
/* The number of devices playing/capturing non-empty stream(s). */
static int non_empty_device_count = 0;
-/* Gets the master device which the stream is attached to. */
-static inline struct cras_iodev *get_master_dev(const struct dev_stream *stream)
+/* The timestamp of last EIO error time. */
+static struct timespec last_io_err_time = { 0, 0 };
+
+/* The gap time to avoid repeated error close request to main thread. */
+static const int ERROR_CLOSE_GAP_TIME_SECS = 10;
+
+/* Gets the main device which the stream is attached to. */
+static inline struct cras_iodev *get_main_dev(const struct dev_stream *stream)
{
- return (struct cras_iodev *)stream->stream->master_dev.dev_ptr;
+ return (struct cras_iodev *)stream->stream->main_dev.dev_ptr;
}
/* Updates the estimated sample rate of open device to all attached
* streams.
*/
-static void update_estimated_rate(struct open_dev *adev)
+static void update_estimated_rate(struct open_dev *adev,
+ struct open_dev *odev_list,
+ bool self_rate_need_update)
{
- struct cras_iodev *master_dev;
+ struct cras_iodev *main_dev;
struct cras_iodev *dev = adev->dev;
+ struct cras_iodev *tracked_dev = NULL;
struct dev_stream *dev_stream;
+ double dev_rate_ratio;
+ double main_dev_rate_ratio;
+
+ /*
+ * If there is an output device on the same sound card running with the same
+ * sampling rate, use the rate of that output device for this device.
+ */
+ if (dev->direction == CRAS_STREAM_INPUT &&
+ cras_iodev_is_on_internal_card(dev->active_node)) {
+ struct open_dev *odev;
+ DL_FOREACH (odev_list, odev) {
+ if (!cras_iodev_is_on_internal_card(
+ odev->dev->active_node))
+ continue;
+ if (odev->dev->format->frame_rate !=
+ dev->format->frame_rate)
+ continue;
+ tracked_dev = odev->dev;
+ break;
+ }
+ }
+
+ /*
+ * Self-owned rate esimator does not need to udpate rate. There is no tracked
+ * output device. So there is no need to update.
+ */
+ if (!self_rate_need_update && !tracked_dev)
+ return;
DL_FOREACH (dev->streams, dev_stream) {
- master_dev = get_master_dev(dev_stream);
- if (master_dev == NULL) {
- syslog(LOG_ERR, "Fail to find master open dev.");
+ main_dev = get_main_dev(dev_stream);
+ if (main_dev == NULL) {
+ syslog(LOG_ERR, "Fail to find main open dev.");
continue;
}
- dev_stream_set_dev_rate(
- dev_stream, dev->format->frame_rate,
- cras_iodev_get_est_rate_ratio(dev),
- cras_iodev_get_est_rate_ratio(master_dev),
- adev->coarse_rate_adjust);
+ if (tracked_dev) {
+ dev_rate_ratio =
+ cras_iodev_get_est_rate_ratio(tracked_dev);
+ main_dev_rate_ratio = dev_rate_ratio;
+ } else {
+ dev_rate_ratio = cras_iodev_get_est_rate_ratio(dev);
+ main_dev_rate_ratio =
+ cras_iodev_get_est_rate_ratio(main_dev);
+ }
+
+ dev_stream_set_dev_rate(dev_stream, dev->format->frame_rate,
+ dev_rate_ratio, main_dev_rate_ratio,
+ adev->coarse_rate_adjust);
}
}
@@ -464,7 +510,7 @@ static int set_input_dev_wake_ts(struct open_dev *adev, bool *need_to_drop)
* adev - The device to capture samples from.
* Returns 0 on success.
*/
-static int capture_to_streams(struct open_dev *adev)
+static int capture_to_streams(struct open_dev *adev, struct open_dev *odev_list)
{
struct cras_iodev *idev = adev->dev;
snd_pcm_uframes_t remainder, hw_level, cap_limit;
@@ -486,14 +532,29 @@ static int capture_to_streams(struct open_dev *adev)
ATLOG(atlog, AUDIO_THREAD_READ_AUDIO_TSTAMP, idev->info.idx,
hw_tstamp.tv_sec, hw_tstamp.tv_nsec);
if (timespec_is_nonzero(&hw_tstamp)) {
+ bool self_rate_need_update;
+
if (hw_level < idev->min_cb_level / 2)
adev->coarse_rate_adjust = 1;
else if (hw_level > idev->max_cb_level * 2)
adev->coarse_rate_adjust = -1;
else
adev->coarse_rate_adjust = 0;
- if (cras_iodev_update_rate(idev, hw_level, &hw_tstamp))
- update_estimated_rate(adev);
+
+ /*
+ * This values means whether the rate estimator in the device
+ * wants to update estimated rate.
+ */
+ self_rate_need_update =
+ !!cras_iodev_update_rate(idev, hw_level, &hw_tstamp);
+
+ /*
+ * Always calls update_estimated_rate so that new output rate
+ * has a chance to propagate to input. In update_estimated_rate,
+ * it will decide whether the new rate is from self rate estimator
+ * or from the tracked output device.
+ */
+ update_estimated_rate(adev, odev_list, self_rate_need_update);
}
cap_limit = get_stream_limit(adev, hw_level, &cap_limit_stream);
@@ -579,12 +640,13 @@ static int capture_to_streams(struct open_dev *adev)
* write_limit - The maximum number of frames to write to dst.
*
* Returns:
- * The number of frames rendered on success, a negative error code otherwise.
+ * The number of frames rendered on success.
* This number of frames is the minimum of the amount of frames each stream
* could provide which is the maximum that can currently be rendered.
*/
-static int write_streams(struct open_dev **odevs, struct open_dev *adev,
- uint8_t *dst, size_t write_limit)
+static unsigned int write_streams(struct open_dev **odevs,
+ struct open_dev *adev, uint8_t *dst,
+ size_t write_limit)
{
struct cras_iodev *odev = adev->dev;
struct dev_stream *curr;
@@ -746,7 +808,7 @@ int write_output_samples(struct open_dev **odevs, struct open_dev *adev,
adev->coarse_rate_adjust = 0;
if (cras_iodev_update_rate(odev, hw_level, &hw_tstamp))
- update_estimated_rate(adev);
+ update_estimated_rate(adev, NULL, true);
}
ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO, adev->dev->info.idx, hw_level,
odev->min_cb_level);
@@ -768,9 +830,6 @@ int write_output_samples(struct open_dev **odevs, struct open_dev *adev,
/* TODO(dgreid) - This assumes interleaved audio. */
dst = area->channels[0].buf;
written = write_streams(odevs, adev, dst, frames);
- if (written < 0) /* pcm has been closed */
- return (int)written;
-
if (written < (snd_pcm_sframes_t)frames)
/* Got all the samples from client that we can, but it
* won't fill the request. */
@@ -934,27 +993,46 @@ int dev_io_send_captured_samples(struct open_dev *idev_list)
static void handle_dev_err(int err_rc, struct open_dev **odevs,
struct open_dev *adev)
{
+ struct timespec diff, now;
if (err_rc == -EPIPE) {
/* Handle severe underrun. */
ATLOG(atlog, AUDIO_THREAD_SEVERE_UNDERRUN, adev->dev->info.idx,
0, 0);
cras_iodev_reset_request(adev->dev);
cras_audio_thread_event_severe_underrun();
+ } else if (err_rc == -EIO) {
+ syslog(LOG_WARNING, "I/O err, reseting %s dev %s",
+ adev->dev->direction == CRAS_STREAM_OUTPUT ? "output" :
+ "input",
+ adev->dev->info.name);
+ clock_gettime(CLOCK_REALTIME, &now);
+ subtract_timespecs(&now, &last_io_err_time, &diff);
+ if ((last_io_err_time.tv_sec == 0 &&
+ last_io_err_time.tv_nsec == 0) ||
+ diff.tv_sec > ERROR_CLOSE_GAP_TIME_SECS)
+ cras_iodev_reset_request(adev->dev);
+ else
+ cras_device_monitor_error_close(adev->dev->info.idx);
+
+ last_io_err_time = now;
+ } else {
+ syslog(LOG_ERR, "Dev %s err %d", adev->dev->info.name, err_rc);
}
/* Device error, remove it. */
dev_io_rm_open_dev(odevs, adev);
}
-int dev_io_capture(struct open_dev **list)
+int dev_io_capture(struct open_dev **list, struct open_dev **olist)
{
struct open_dev *idev_list = *list;
+ struct open_dev *odev_list = *olist;
struct open_dev *adev;
int rc;
DL_FOREACH (idev_list, adev) {
if (!cras_iodev_is_open(adev->dev))
continue;
- rc = capture_to_streams(adev);
+ rc = capture_to_streams(adev, odev_list);
if (rc < 0)
handle_dev_err(rc, list, adev);
}
@@ -1105,7 +1183,7 @@ void dev_io_run(struct open_dev **odevs, struct open_dev **idevs,
update_longest_wake(*idevs, &now);
dev_io_playback_fetch(*odevs);
- dev_io_capture(idevs);
+ dev_io_capture(idevs, odevs);
dev_io_send_captured_samples(*idevs);
dev_io_playback_write(odevs, output_converter);
}
@@ -1259,14 +1337,61 @@ static void delete_stream_from_dev(struct cras_iodev *dev,
dev_stream_destroy(out);
}
-int dev_io_append_stream(struct open_dev **dev_list,
+/*
+ * Finds a matched input stream from open device list.
+ * The definition of the matched streams: Two streams having
+ * the same sampling rate and the same cb_threshold.
+ * This means their sleep time intervals should be very close
+ * if we neglect device estimated rate.
+ */
+static struct dev_stream *
+find_matched_input_stream(const struct cras_rstream *out_stream,
+ struct open_dev *odev_list)
+{
+ struct open_dev *odev;
+ struct dev_stream *dev_stream;
+ size_t out_rate = out_stream->format.frame_rate;
+ size_t out_cb_threshold = cras_rstream_get_cb_threshold(out_stream);
+
+ DL_FOREACH (odev_list, odev) {
+ DL_FOREACH (odev->dev->streams, dev_stream) {
+ if (dev_stream->stream->format.frame_rate != out_rate)
+ continue;
+ if (cras_rstream_get_cb_threshold(dev_stream->stream) !=
+ out_cb_threshold)
+ continue;
+ return dev_stream;
+ }
+ }
+ return NULL;
+}
+
+static bool
+find_matched_input_stream_next_cb_ts(const struct cras_rstream *stream,
+ struct open_dev *odev_list,
+ const struct timespec **next_cb_ts,
+ const struct timespec **sleep_interval_ts)
+{
+ struct dev_stream *dev_stream =
+ find_matched_input_stream(stream, odev_list);
+ if (dev_stream) {
+ *next_cb_ts = dev_stream_next_cb_ts(dev_stream);
+ *sleep_interval_ts = dev_stream_sleep_interval_ts(dev_stream);
+ return *next_cb_ts != NULL;
+ }
+ return false;
+}
+
+int dev_io_append_stream(struct open_dev **odevs, struct open_dev **idevs,
struct cras_rstream *stream,
struct cras_iodev **iodevs, unsigned int num_iodevs)
{
+ struct open_dev **dev_list;
struct open_dev *open_dev;
struct cras_iodev *dev;
struct dev_stream *out;
struct timespec init_cb_ts;
+ const struct timespec *init_sleep_interval_ts = NULL;
struct timespec extra_sleep;
const struct timespec *stream_ts;
unsigned int i;
@@ -1274,6 +1399,11 @@ int dev_io_append_stream(struct open_dev **dev_list,
int level;
int rc = 0;
+ if (stream->direction == CRAS_STREAM_OUTPUT)
+ dev_list = odevs;
+ else
+ dev_list = idevs;
+
for (i = 0; i < num_iodevs; i++) {
DL_SEARCH_SCALAR(*dev_list, open_dev, dev, iodevs[i]);
if (!open_dev)
@@ -1318,35 +1448,55 @@ int dev_io_append_stream(struct open_dev **dev_list,
* may cause device buffer level stack up.
*/
if (stream->direction == CRAS_STREAM_OUTPUT) {
- DL_FOREACH (dev->streams, out) {
- stream_ts = dev_stream_next_cb_ts(out);
- if (stream_ts &&
- (!cb_ts_set ||
- timespec_after(&init_cb_ts, stream_ts))) {
- init_cb_ts = *stream_ts;
- cb_ts_set = true;
+ /*
+ * If there is a matched input stream, find its next cb time.
+ * Use that as the initial cb time for this output stream.
+ */
+ const struct timespec *in_stream_ts;
+ const struct timespec *in_stream_sleep_interval_ts;
+ bool found_matched_input;
+ found_matched_input =
+ find_matched_input_stream_next_cb_ts(
+ stream, *idevs, &in_stream_ts,
+ &in_stream_sleep_interval_ts);
+ if (found_matched_input) {
+ init_cb_ts = *in_stream_ts;
+ init_sleep_interval_ts =
+ in_stream_sleep_interval_ts;
+ } else {
+ DL_FOREACH (dev->streams, out) {
+ stream_ts = dev_stream_next_cb_ts(out);
+ if (stream_ts &&
+ (!cb_ts_set ||
+ timespec_after(&init_cb_ts,
+ stream_ts))) {
+ init_cb_ts = *stream_ts;
+ cb_ts_set = true;
+ }
}
- }
- if (!cb_ts_set) {
- level = cras_iodev_get_valid_frames(
- dev, &init_cb_ts);
- if (level < 0) {
- syslog(LOG_ERR,
- "Failed to set output init_cb_ts, rc = %d",
- level);
- rc = -EINVAL;
- break;
+ if (!cb_ts_set) {
+ level = cras_iodev_get_valid_frames(
+ dev, &init_cb_ts);
+ if (level < 0) {
+ syslog(LOG_ERR,
+ "Failed to set output init_cb_ts, rc = %d",
+ level);
+ rc = -EINVAL;
+ break;
+ }
+ level -= cras_frames_at_rate(
+ stream->format.frame_rate,
+ cras_rstream_get_cb_threshold(
+ stream),
+ dev->format->frame_rate);
+ if (level < 0)
+ level = 0;
+ cras_frames_to_time(
+ level, dev->format->frame_rate,
+ &extra_sleep);
+ add_timespecs(&init_cb_ts,
+ &extra_sleep);
}
- level -= cras_frames_at_rate(
- stream->format.frame_rate,
- cras_rstream_get_cb_threshold(stream),
- dev->format->frame_rate);
- if (level < 0)
- level = 0;
- cras_frames_to_time(level,
- dev->format->frame_rate,
- &extra_sleep);
- add_timespecs(&init_cb_ts, &extra_sleep);
}
} else {
/*
@@ -1365,7 +1515,7 @@ int dev_io_append_stream(struct open_dev **dev_list,
}
out = dev_stream_create(stream, dev->info.idx, dev->format, dev,
- &init_cb_ts);
+ &init_cb_ts, init_sleep_interval_ts);
if (!out) {
rc = -EINVAL;
break;
@@ -1418,20 +1568,6 @@ int dev_io_remove_stream(struct open_dev **dev_list,
struct cras_rstream *stream, struct cras_iodev *dev)
{
struct open_dev *open_dev;
- struct timespec delay;
- unsigned fetch_delay_msec;
-
- /* Metrics log the longest fetch delay of this stream. */
- if (timespec_after(&stream->longest_fetch_interval,
- &stream->sleep_interval_ts)) {
- subtract_timespecs(&stream->longest_fetch_interval,
- &stream->sleep_interval_ts, &delay);
- fetch_delay_msec =
- delay.tv_sec * 1000 + delay.tv_nsec / 1000000;
- if (fetch_delay_msec)
- cras_server_metrics_longest_fetch_delay(
- fetch_delay_msec);
- }
ATLOG(atlog, AUDIO_THREAD_STREAM_REMOVED, stream->stream_id, 0, 0);
diff --git a/cras/src/server/dev_io.h b/cras/src/server/dev_io.h
index 259bbabd..ca71a809 100644
--- a/cras/src/server/dev_io.h
+++ b/cras/src/server/dev_io.h
@@ -58,8 +58,9 @@ int write_output_samples(struct open_dev **odevs, struct open_dev *adev,
* Captures samples from each device in the list.
* list - Pointer to the list of input devices. Devices that fail to read
* will be removed from the list.
+ * olist - Pointer to the list of output devices.
*/
-int dev_io_capture(struct open_dev **list);
+int dev_io_capture(struct open_dev **list, struct open_dev **olist);
/*
* Send samples that have been captured to their streams.
@@ -101,7 +102,7 @@ struct open_dev *dev_io_find_open_dev(struct open_dev *odev_list,
unsigned int dev_idx);
/* Append a new stream to a specified set of iodevs. */
-int dev_io_append_stream(struct open_dev **dev_list,
+int dev_io_append_stream(struct open_dev **odevs, struct open_dev **idevs,
struct cras_rstream *stream,
struct cras_iodev **iodevs, unsigned int num_iodevs);
diff --git a/cras/src/server/dev_stream.c b/cras/src/server/dev_stream.c
index 025aeddd..be5a6dab 100644
--- a/cras/src/server/dev_stream.c
+++ b/cras/src/server/dev_stream.c
@@ -63,7 +63,8 @@ unsigned int max_frames_for_conversion(unsigned int stream_frames,
struct dev_stream *dev_stream_create(struct cras_rstream *stream,
unsigned int dev_id,
const struct cras_audio_format *dev_fmt,
- void *dev_ptr, struct timespec *cb_ts)
+ void *dev_ptr, struct timespec *cb_ts,
+ const struct timespec *sleep_interval_ts)
{
struct dev_stream *out;
struct cras_audio_format *stream_fmt = &stream->format;
@@ -122,8 +123,15 @@ struct dev_stream *dev_stream_create(struct cras_rstream *stream,
out->conv_buffer = byte_buffer_create(buf_bytes);
out->conv_area = cras_audio_area_create(ofmt->num_channels);
- cras_frames_to_time(cras_rstream_get_cb_threshold(stream),
- stream_fmt->frame_rate, &stream->sleep_interval_ts);
+ /* Use sleep interval hint from argument if it is provided */
+ if (sleep_interval_ts) {
+ stream->sleep_interval_ts = *sleep_interval_ts;
+ } else {
+ cras_frames_to_time(cras_rstream_get_cb_threshold(stream),
+ stream_fmt->frame_rate,
+ &stream->sleep_interval_ts);
+ }
+
stream->next_cb_ts = *cb_ts;
/* Sets up the stream & dev pair. */
@@ -149,9 +157,9 @@ void dev_stream_destroy(struct dev_stream *dev_stream)
void dev_stream_set_dev_rate(struct dev_stream *dev_stream,
unsigned int dev_rate, double dev_rate_ratio,
- double master_rate_ratio, int coarse_rate_adjust)
+ double main_rate_ratio, int coarse_rate_adjust)
{
- if (dev_stream->dev_id == dev_stream->stream->master_dev.dev_id) {
+ if (dev_stream->dev_id == dev_stream->stream->main_dev.dev_id) {
cras_fmt_conv_set_linear_resample_rates(dev_stream->conv,
dev_rate, dev_rate);
cras_frames_to_time_precise(
@@ -159,9 +167,8 @@ void dev_stream_set_dev_rate(struct dev_stream *dev_stream,
dev_stream->stream->format.frame_rate * dev_rate_ratio,
&dev_stream->stream->sleep_interval_ts);
} else {
- double new_rate =
- dev_rate * dev_rate_ratio / master_rate_ratio +
- coarse_rate_adjust_step * coarse_rate_adjust;
+ double new_rate = dev_rate * dev_rate_ratio / main_rate_ratio +
+ coarse_rate_adjust_step * coarse_rate_adjust;
cras_fmt_conv_set_linear_resample_rates(dev_stream->conv,
dev_rate, new_rate);
}
diff --git a/cras/src/server/dev_stream.h b/cras/src/server/dev_stream.h
index c39a8017..6b34d5d7 100644
--- a/cras/src/server/dev_stream.h
+++ b/cras/src/server/dev_stream.h
@@ -46,30 +46,47 @@ struct dev_stream {
int is_running;
};
+/*
+ * Creates a dev_stream.
+ *
+ * Args:
+ * stream - The associated rstream.
+ * dev_id - Index of the device.
+ * dev_fmt - The format of the device.
+ * dev_ptr - A pointer to the device
+ * cb_ts - A pointer to the initial callback time.
+ * sleep_interval_ts - A pointer to the initial sleep interval.
+ * Set to null to calculate the value from device rate and block size.
+ * Note that we need this argument so that output device sleep interval
+ * can use input device sleep interval in the beginning to have perfect
+ * alignment in WebRTC use case.
+ * Returns the pointer to the created dev_stream.
+ */
struct dev_stream *dev_stream_create(struct cras_rstream *stream,
unsigned int dev_id,
const struct cras_audio_format *dev_fmt,
- void *dev_ptr, struct timespec *cb_ts);
+ void *dev_ptr, struct timespec *cb_ts,
+ const struct timespec *sleep_interval_ts);
void dev_stream_destroy(struct dev_stream *dev_stream);
/*
* Update the estimated sample rate of the device. For multiple active
* devices case, the linear resampler will be configured by the estimated
- * rate ration of the master device and the current active device the
+ * rate ration of the main device and the current active device the
* rstream attaches to.
*
* Args:
* dev_stream - The structure holding the stream.
* dev_rate - The sample rate device is using.
* dev_rate_ratio - The ratio of estimated rate and used rate.
- * master_rate_ratio - The ratio of estimated rate and used rate of
- * master device.
+ * main_rate_ratio - The ratio of estimated rate and used rate of
+ * main device.
* coarse_rate_adjust - The flag to indicate the direction device
* sample rate should adjust to.
*/
void dev_stream_set_dev_rate(struct dev_stream *dev_stream,
unsigned int dev_rate, double dev_rate_ratio,
- double master_rate_ratio, int coarse_rate_adjust);
+ double main_rate_ratio, int coarse_rate_adjust);
/*
* Renders count frames from shm into dst. Updates count if anything is
diff --git a/cras/src/server/server_stream.c b/cras/src/server/server_stream.c
index 6644c469..36d5496e 100644
--- a/cras/src/server/server_stream.c
+++ b/cras/src/server/server_stream.c
@@ -83,6 +83,5 @@ void server_stream_destroy(struct stream_list *stream_list,
syslog(LOG_ERR, "No server stream to destroy");
return;
}
- /* Schedule remove stream in next main thread loop. */
- cras_system_add_task(server_stream_rm_cb, stream_list);
+ server_stream_rm_cb(stream_list);
}
diff --git a/cras/src/server/stream_list.c b/cras/src/server/stream_list.c
index 719608a4..04ef9fe1 100644
--- a/cras/src/server/stream_list.c
+++ b/cras/src/server/stream_list.c
@@ -3,12 +3,19 @@
* found in the LICENSE file.
*/
+#include <syslog.h>
#include "cras_rstream.h"
#include "cras_tm.h"
#include "cras_types.h"
#include "stream_list.h"
#include "utlist.h"
+/*
+ * If the time difference of two streams is short than 10s, they may be the RTC
+ * streams.
+ */
+static const struct timespec RTC_STREAM_THRESHOLD = { 10, 0 };
+
struct stream_list {
struct cras_rstream *streams;
struct cras_rstream *streams_to_delete;
@@ -154,3 +161,28 @@ bool stream_list_has_pinned_stream(struct stream_list *list,
}
return false;
}
+
+void detect_rtc_stream_pair(struct stream_list *list,
+ struct cras_rstream *stream)
+{
+ struct cras_rstream *next_stream;
+ if (stream->cb_threshold != 480)
+ return;
+ if (stream->client_type != CRAS_CLIENT_TYPE_CHROME &&
+ stream->client_type != CRAS_CLIENT_TYPE_LACROS)
+ return;
+ DL_FOREACH (list->streams, next_stream) {
+ if (next_stream->cb_threshold == 480 &&
+ next_stream->direction != stream->direction &&
+ next_stream->client_type == stream->client_type &&
+ timespec_diff_shorter_than(&stream->start_ts,
+ &next_stream->start_ts,
+ &RTC_STREAM_THRESHOLD)) {
+ stream->stream_type =
+ CRAS_STREAM_TYPE_VOICE_COMMUNICATION;
+ next_stream->stream_type =
+ CRAS_STREAM_TYPE_VOICE_COMMUNICATION;
+ return;
+ }
+ }
+}
diff --git a/cras/src/server/stream_list.h b/cras/src/server/stream_list.h
index 0a9b86a2..a527bc97 100644
--- a/cras/src/server/stream_list.h
+++ b/cras/src/server/stream_list.h
@@ -55,3 +55,14 @@ int stream_list_rm_all_client_streams(struct stream_list *list,
*/
bool stream_list_has_pinned_stream(struct stream_list *list,
unsigned int dev_idx);
+
+/*
+ * Detects whether there is a RTC stream pair based on these rules:
+ * 1. The cb_threshold is 480.
+ * 2. The direction of two streams are opposite.
+ * 3. Two streams are from the same client. (Chrome or LaCrOS)
+ * 4. The start time of two streams are close enough. (shorter than 1s)
+ * If all rules are passed, set the stream type to the voice communication.
+ */
+void detect_rtc_stream_pair(struct stream_list *list,
+ struct cras_rstream *stream);
diff --git a/cras/src/tests/a2dp_iodev_unittest.cc b/cras/src/tests/a2dp_iodev_unittest.cc
index 523a62e4..06c1cd3c 100644
--- a/cras/src/tests/a2dp_iodev_unittest.cc
+++ b/cras/src/tests/a2dp_iodev_unittest.cc
@@ -803,6 +803,10 @@ const char* cras_bt_device_object_path(const struct cras_bt_device* device) {
return "/org/bluez/hci0/dev_1A_2B_3C_4D_5E_6F";
}
+int cras_bt_device_get_stable_id(const struct cras_bt_device* device) {
+ return 123;
+}
+
void cras_bt_device_append_iodev(struct cras_bt_device* device,
struct cras_iodev* iodev,
enum cras_bt_device_profile profile) {
diff --git a/cras/src/tests/alsa_io_unittest.cc b/cras/src/tests/alsa_io_unittest.cc
index b3059a23..021b4789 100644
--- a/cras/src/tests/alsa_io_unittest.cc
+++ b/cras/src/tests/alsa_io_unittest.cc
@@ -2559,6 +2559,10 @@ void cras_system_set_volume_limits(long min, long max) {
sys_set_volume_limits_called++;
}
+bool cras_system_get_noise_cancellation_enabled() {
+ return false;
+}
+
// From cras_alsa_mixer.
void cras_alsa_mixer_set_dBFS(struct cras_alsa_mixer* m,
long dB_level,
@@ -2807,6 +2811,17 @@ int ucm_get_channels_for_dev(struct cras_use_case_mgr* mgr,
return -EINVAL;
}
+int ucm_node_noise_cancellation_exists(struct cras_use_case_mgr* mgr,
+ const char* node_name) {
+ return 0;
+}
+
+int ucm_enable_node_noise_cancellation(struct cras_use_case_mgr* mgr,
+ const char* node_name,
+ int enable) {
+ return 0;
+}
+
struct cras_volume_curve* cras_volume_curve_create_default() {
return &default_curve;
}
@@ -2888,6 +2903,12 @@ const char* cras_alsa_jack_get_ucm_device(const struct cras_alsa_jack* jack) {
return NULL;
}
+void ucm_disable_all_hotword_models(struct cras_use_case_mgr* mgr) {}
+
+int ucm_enable_hotword_model(struct cras_use_case_mgr* mgr) {
+ return 0;
+}
+
int ucm_get_default_node_gain(struct cras_use_case_mgr* mgr,
const char* dev,
long* gain) {
diff --git a/cras/src/tests/alsa_mixer_unittest.cc b/cras/src/tests/alsa_mixer_unittest.cc
index edf61101..b3db9de5 100644
--- a/cras/src/tests/alsa_mixer_unittest.cc
+++ b/cras/src/tests/alsa_mixer_unittest.cc
@@ -381,7 +381,7 @@ TEST(AlsaMixer, CreateOneUnknownElementWithVolume) {
mixer_control_destroy(mixer_output);
}
-TEST(AlsaMixer, CreateOneMasterElement) {
+TEST(AlsaMixer, CreateOneMainElement) {
struct cras_alsa_mixer* c;
int element_playback_volume[] = {
1,
@@ -419,10 +419,10 @@ TEST(AlsaMixer, CreateOneMasterElement) {
EXPECT_EQ(3, snd_mixer_selem_get_name_called);
EXPECT_EQ(1, snd_mixer_elem_next_called);
- /* set mute should be called for Master. */
+ /* set mute should be called for Main. */
cras_alsa_mixer_set_mute(c, 0, NULL);
EXPECT_EQ(1, snd_mixer_selem_set_playback_switch_all_called);
- /* set volume should be called for Master. */
+ /* set volume should be called for Main. */
cras_alsa_mixer_set_dBFS(c, 0, NULL);
EXPECT_EQ(1, snd_mixer_selem_set_playback_dB_all_called);
@@ -515,15 +515,15 @@ TEST(AlsaMixer, CreateTwoMainVolumeElements) {
EXPECT_EQ(5, snd_mixer_selem_get_name_called);
EXPECT_EQ(3, snd_mixer_selem_has_playback_switch_called);
- /* Set mute should be called for Master only. */
+ /* Set mute should be called for Main only. */
cras_alsa_mixer_set_mute(c, 0, NULL);
EXPECT_EQ(1, snd_mixer_selem_set_playback_switch_all_called);
- /* Set volume should be called for Master and PCM. If Master doesn't set to
+ /* Set volume should be called for Main and PCM. If Main doesn't set to
* anything but zero then the entire volume should be passed to the PCM
* control.*/
- /* Set volume should be called for Master and PCM. (without mixer_output) */
+ /* Set volume should be called for Main and PCM. (without mixer_output) */
snd_mixer_selem_get_playback_dB_return_values = get_dB_returns;
snd_mixer_selem_get_playback_dB_return_values_length =
ARRAY_SIZE(get_dB_returns);
@@ -557,8 +557,8 @@ TEST(AlsaMixer, CreateTwoMainVolumeElements) {
EXPECT_EQ(1, snd_mixer_selem_has_playback_switch_called);
EXPECT_EQ(1, snd_mixer_selem_get_playback_dB_range_called);
- /* Set volume should be called for Master, PCM, and the mixer_output passed
- * in. If Master doesn't set to anything but zero then the entire volume
+ /* Set volume should be called for Main, PCM, and the mixer_output passed
+ * in. If Main doesn't set to anything but zero then the entire volume
* should be passed to the PCM control.*/
cras_alsa_mixer_set_dBFS(c, -50, mixer_output);
EXPECT_EQ(3, snd_mixer_selem_set_playback_dB_all_called);
@@ -566,8 +566,8 @@ TEST(AlsaMixer, CreateTwoMainVolumeElements) {
EXPECT_EQ(30, set_dB_values[0]);
EXPECT_EQ(30, set_dB_values[1]);
EXPECT_EQ(30, set_dB_values[2]);
- /* Set volume should be called for Master and PCM. Since the controls were
- * sorted, Master should get the volume remaining after PCM is set, in this
+ /* Set volume should be called for Main and PCM. Since the controls were
+ * sorted, Main should get the volume remaining after PCM is set, in this
* case -50 - -24 = -26. */
long get_dB_returns2[] = {
-25,
@@ -584,7 +584,7 @@ TEST(AlsaMixer, CreateTwoMainVolumeElements) {
cras_alsa_mixer_set_dBFS(c, -50, mixer_output);
EXPECT_EQ(2, snd_mixer_selem_set_playback_dB_all_called);
EXPECT_EQ(2, snd_mixer_selem_get_playback_dB_called);
- EXPECT_EQ(54, set_dB_values[0]); // Master
+ EXPECT_EQ(54, set_dB_values[0]); // Main
EXPECT_EQ(30, set_dB_values[1]); // PCM
cras_alsa_mixer_destroy(c);
@@ -639,7 +639,7 @@ TEST(AlsaMixer, CreateTwoMainCaptureElements) {
EXPECT_EQ(5, snd_mixer_selem_get_name_called);
EXPECT_EQ(3, snd_mixer_selem_has_capture_switch_called);
- /* Set mute should be called for Master only. */
+ /* Set mute should be called for Main only. */
cras_alsa_mixer_set_capture_mute(c, 0, NULL);
EXPECT_EQ(1, snd_mixer_selem_set_capture_switch_all_called);
/* Set volume should be called for Capture and Digital Capture. If Capture
@@ -773,7 +773,7 @@ class AlsaMixerOutputs : public testing::Test {
ResetStubData();
snd_mixer_first_elem_return_value =
- reinterpret_cast<snd_mixer_elem_t*>(1); // Master
+ reinterpret_cast<snd_mixer_elem_t*>(1); // Main
snd_mixer_elem_next_return_values = elements;
snd_mixer_elem_next_return_values_length = ARRAY_SIZE(elements);
snd_mixer_selem_has_playback_volume_return_values = element_playback_volume;
diff --git a/cras/src/tests/alsa_ucm_unittest.cc b/cras/src/tests/alsa_ucm_unittest.cc
index 44c35879..1b351ddf 100644
--- a/cras/src/tests/alsa_ucm_unittest.cc
+++ b/cras/src/tests/alsa_ucm_unittest.cc
@@ -28,11 +28,13 @@ static unsigned snd_use_case_get_called;
static std::vector<std::string> snd_use_case_get_id;
static int snd_use_case_set_return;
static std::map<std::string, std::string> snd_use_case_get_value;
+static std::map<std::string, unsigned> snd_use_case_geti_value;
static unsigned snd_use_case_set_called;
static std::vector<std::pair<std::string, std::string> > snd_use_case_set_param;
static std::map<std::string, const char**> fake_list;
static std::map<std::string, unsigned> fake_list_size;
static unsigned snd_use_case_free_list_called;
+static unsigned snd_use_case_geti_called;
static std::vector<std::string> list_devices_callback_names;
static std::vector<void*> list_devices_callback_args;
static struct cras_use_case_mgr cras_ucm_mgr;
@@ -45,10 +47,12 @@ static void ResetStubData() {
snd_use_case_set_return = 0;
snd_use_case_get_called = 0;
snd_use_case_set_called = 0;
+ snd_use_case_geti_called = 0;
snd_use_case_set_param.clear();
snd_use_case_free_list_called = 0;
snd_use_case_get_id.clear();
snd_use_case_get_value.clear();
+ snd_use_case_geti_value.clear();
fake_list.clear();
fake_list_size.clear();
fake_list["_verbs"] = avail_verbs;
@@ -57,6 +61,7 @@ static void ResetStubData() {
list_devices_callback_args.clear();
snd_use_case_mgr_open_mgr_ptr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
cras_ucm_mgr.use_case = CRAS_STREAM_TYPE_DEFAULT;
+ cras_ucm_mgr.hotword_modifier = NULL;
}
static void list_devices_callback(const char* section_name, void* arg) {
@@ -522,26 +527,85 @@ TEST(AlsaUcm, SetHotwordModel) {
const char* modifiers[] = {"Hotword Model en", "Comment1",
"Hotword Model jp", "Comment2",
"Hotword Model de", "Comment3"};
- const char* enabled_mods[] = {"Hotword Model en"};
+ const char* enabled_mods[] = {"Hotword Model jp"};
+ int ret;
+ std::string id = "_modstatus/Hotword Model jp";
ResetStubData();
+ snd_use_case_geti_value[id] = 1;
fake_list["_modifiers/HiFi"] = modifiers;
fake_list_size["_modifiers/HiFi"] = 6;
EXPECT_EQ(-EINVAL, ucm_set_hotword_model(mgr, "zh"));
EXPECT_EQ(0, snd_use_case_set_called);
+ ret = ucm_set_hotword_model(mgr, "jp");
+
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, snd_use_case_set_called);
+ EXPECT_EQ(0, strcmp(mgr->hotword_modifier, "Hotword Model jp"));
+
fake_list["_enamods"] = enabled_mods;
fake_list_size["_enamods"] = 1;
- ucm_set_hotword_model(mgr, "jp");
-
+ ret = ucm_set_hotword_model(mgr, "de");
+ EXPECT_EQ(0, ret);
EXPECT_EQ(2, snd_use_case_set_called);
+ EXPECT_EQ(1, snd_use_case_geti_called);
EXPECT_EQ(
snd_use_case_set_param[0],
- std::make_pair(std::string("_dismod"), std::string("Hotword Model en")));
+ std::make_pair(std::string("_dismod"), std::string("Hotword Model jp")));
EXPECT_EQ(
snd_use_case_set_param[1],
- std::make_pair(std::string("_enamod"), std::string("Hotword Model jp")));
+ std::make_pair(std::string("_enamod"), std::string("Hotword Model de")));
+ free(mgr->hotword_modifier);
+}
+
+TEST(AlsaUcm, DisableAllHotwordModels) {
+ struct cras_use_case_mgr* mgr = &cras_ucm_mgr;
+ const char* modifiers[] = {"Hotword Model en", "Comment1",
+ "Hotword Model jp", "Comment2",
+ "Hotword Model de", "Comment3"};
+ const char* enabled_mods[] = {"Hotword Model en"};
+ ResetStubData();
+
+ fake_list["_modifiers/HiFi"] = modifiers;
+ fake_list_size["_modifiers/HiFi"] = 6;
+ fake_list["_enamods"] = enabled_mods;
+ fake_list_size["_enamods"] = 1;
+
+ ucm_disable_all_hotword_models(mgr);
+
+ EXPECT_EQ(1, snd_use_case_set_called);
+ EXPECT_EQ(
+ snd_use_case_set_param[0],
+ std::make_pair(std::string("_dismod"), std::string("Hotword Model en")));
+}
+
+TEST(AlsaUcm, EnableHotwordModel) {
+ struct cras_use_case_mgr* mgr = &cras_ucm_mgr;
+ const char* modifiers[] = {"Hotword Model en", "Comment1",
+ "Hotword Model jp", "Comment2",
+ "Hotword Model de", "Comment3"};
+ const char* enabled_mods[] = {""};
+ int ret;
+ ResetStubData();
+
+ fake_list["_modifiers/HiFi"] = modifiers;
+ fake_list_size["_modifiers/HiFi"] = 6;
+ fake_list["_enamods"] = enabled_mods;
+ fake_list_size["_enamods"] = 0;
+
+ EXPECT_EQ(-EINVAL, ucm_enable_hotword_model(mgr));
+
+ mgr->hotword_modifier = strdup("Hotword Model de");
+ ret = ucm_enable_hotword_model(mgr);
+
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(1, snd_use_case_set_called);
+ EXPECT_EQ(
+ snd_use_case_set_param[0],
+ std::make_pair(std::string("_enamod"), std::string("Hotword Model de")));
+ free(mgr->hotword_modifier);
}
TEST(AlsaUcm, SwapModeExists) {
@@ -629,6 +693,76 @@ TEST(AlsaUcm, DisableSwapMode) {
EXPECT_EQ(1, snd_use_case_set_called);
}
+TEST(AlsaUcm, NoiseCancellationExists) {
+ struct cras_use_case_mgr* mgr = &cras_ucm_mgr;
+ int rc;
+ const char* node = "Internal Mic";
+ const char* modifiers_1[] = {"Internal Mic Noise Cancellation", "Comment"};
+ const char* modifiers_2[] = {"Internal Mic Noise Augmentation", "Comment"};
+ const char* modifiers_3[] = {"Microphone Noise Cancellation", "Comment"};
+
+ ResetStubData();
+
+ fake_list["_modifiers/HiFi"] = modifiers_1;
+ fake_list_size["_modifiers/HiFi"] = 2;
+ rc = ucm_node_noise_cancellation_exists(mgr, node);
+ EXPECT_EQ(1, rc);
+
+ fake_list["_modifiers/HiFi"] = modifiers_2;
+ fake_list_size["_modifiers/HiFi"] = 2;
+ rc = ucm_node_noise_cancellation_exists(mgr, node);
+ EXPECT_EQ(0, rc);
+
+ fake_list["_modifiers/HiFi"] = modifiers_3;
+ fake_list_size["_modifiers/HiFi"] = 2;
+ rc = ucm_node_noise_cancellation_exists(mgr, node);
+ EXPECT_EQ(0, rc);
+}
+
+TEST(AlsaUcm, EnableDisableNoiseCancellation) {
+ struct cras_use_case_mgr* mgr = &cras_ucm_mgr;
+ int rc;
+ const char* modifiers[] = {"Internal Mic Noise Cancellation", "Comment1",
+ "Microphone Noise Cancellation", "Comment2"};
+ const char* modifiers_enabled[] = {"Internal Mic Noise Cancellation"};
+
+ ResetStubData();
+
+ fake_list["_modifiers/HiFi"] = modifiers;
+ fake_list_size["_modifiers/HiFi"] = 4;
+
+ fake_list["_enamods"] = modifiers_enabled;
+ fake_list_size["_enamods"] = 1;
+
+ snd_use_case_set_return = 0;
+
+ rc = ucm_enable_node_noise_cancellation(mgr, "Line In", 1);
+ EXPECT_EQ(-EPERM, rc); // Modifier is not existed
+ EXPECT_EQ(0, snd_use_case_set_called);
+
+ rc = ucm_enable_node_noise_cancellation(mgr, "Line In", 0);
+ EXPECT_EQ(-EPERM, rc); // Modifier is not existed
+ EXPECT_EQ(0, snd_use_case_set_called);
+
+ rc = ucm_enable_node_noise_cancellation(mgr, "Microphone", 0);
+ EXPECT_EQ(0, rc); // Modifier is already disabled
+ EXPECT_EQ(0, snd_use_case_set_called);
+
+ rc = ucm_enable_node_noise_cancellation(mgr, "Microphone", 1);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, snd_use_case_set_called);
+
+ snd_use_case_set_called = 0;
+
+ rc = ucm_enable_node_noise_cancellation(mgr, "Internal Mic", 1);
+ EXPECT_EQ(0, rc); // Modifier is already enabled
+ EXPECT_EQ(0, snd_use_case_set_called);
+
+ rc = ucm_enable_node_noise_cancellation(mgr, "Internal Mic", 0);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, snd_use_case_set_called);
+}
+
TEST(AlsaFlag, GetFlag) {
struct cras_use_case_mgr* mgr = &cras_ucm_mgr;
char* flag_value;
@@ -1406,6 +1540,19 @@ int snd_use_case_free_list(const char* list[], int items) {
return 0;
}
+int snd_use_case_geti(snd_use_case_mgr_t* uc_mgr,
+ const char* identifier,
+ long* value) {
+ snd_use_case_geti_called++;
+ if (snd_use_case_geti_value.find(identifier) ==
+ snd_use_case_geti_value.end()) {
+ *value = 0;
+ return -1;
+ }
+ *value = snd_use_case_geti_value[identifier];
+ return 0;
+}
+
} /* extern "C" */
} // namespace
diff --git a/cras/src/tests/apm_list_unittest.cc b/cras/src/tests/apm_list_unittest.cc
index 09c7b866..65e712fb 100644
--- a/cras/src/tests/apm_list_unittest.cc
+++ b/cras/src/tests/apm_list_unittest.cc
@@ -79,6 +79,64 @@ static void delete_tempdir(char* dir) {
rmdir(dir);
}
+static void init_channel_layout(struct cras_audio_format* fmt) {
+ int i;
+ for (i = 0; i < CRAS_CH_MAX; i++)
+ fmt->channel_layout[i] = -1;
+}
+
+TEST(ApmList, AddApmInputDevUnuseFirstChannel) {
+ struct cras_audio_format fmt;
+ struct cras_audio_format* val;
+ struct cras_apm* apm;
+ int ch;
+ const int num_test_casts = 9;
+ int test_layouts[num_test_casts][CRAS_CH_MAX] = {
+ {0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+ {0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+ {0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+ {1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+ {1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+ {2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+ {2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+ {3, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+ {3, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1}};
+ int test_num_channels[num_test_casts] = {1, 2, 2, 2, 2, 3, 4, 4, 4};
+
+ fmt.frame_rate = 48000;
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+
+ cras_apm_list_init("");
+ list = cras_apm_list_create(stream_ptr, APM_ECHO_CANCELLATION);
+ EXPECT_NE((void*)NULL, list);
+
+ for (int i = 0; i < num_test_casts; i++) {
+ fmt.num_channels = test_num_channels[i];
+ init_channel_layout(&fmt);
+ for (ch = 0; ch < CRAS_CH_MAX; ch++)
+ fmt.channel_layout[ch] = test_layouts[i][ch];
+
+ /* Input dev is of aec use case. */
+ apm = cras_apm_list_add_apm(list, dev_ptr, &fmt, 1);
+ EXPECT_NE((void*)NULL, apm);
+
+ /* Assert that the post-processing format never has an unset
+ * first channel in the layout. */
+ bool first_channel_found_in_layout = 0;
+ val = cras_apm_list_get_format(apm);
+ for (ch = 0; ch < CRAS_CH_MAX; ch++)
+ if (0 == val->channel_layout[ch])
+ first_channel_found_in_layout = 1;
+
+ EXPECT_EQ(1, first_channel_found_in_layout);
+
+ cras_apm_list_remove_apm(list, dev_ptr);
+ }
+
+ cras_apm_list_destroy(list);
+ cras_apm_list_deinit();
+}
+
TEST(ApmList, AddRemoveApm) {
struct cras_audio_format fmt;
char* dir;
@@ -170,6 +228,9 @@ TEST(ApmList, ApmProcessForwardBuffer) {
fmt.num_channels = 2;
fmt.frame_rate = 48000;
fmt.format = SND_PCM_FORMAT_S16_LE;
+ init_channel_layout(&fmt);
+ fmt.channel_layout[CRAS_CH_FL] = 0;
+ fmt.channel_layout[CRAS_CH_FR] = 1;
cras_apm_list_init("");
diff --git a/cras/src/tests/audio_thread_unittest.cc b/cras/src/tests/audio_thread_unittest.cc
index 6b78c696..93045e0b 100644
--- a/cras/src/tests/audio_thread_unittest.cc
+++ b/cras/src/tests/audio_thread_unittest.cc
@@ -56,6 +56,7 @@ static struct cras_iodev* cras_iodev_start_ramp_odev;
static enum CRAS_IODEV_RAMP_REQUEST cras_iodev_start_ramp_request;
static struct timespec clock_gettime_retspec;
static struct timespec init_cb_ts_;
+static struct timespec sleep_interval_ts_;
static std::map<const struct dev_stream*, struct timespec>
dev_stream_wake_time_val;
static int cras_device_monitor_set_device_mute_state_called;
@@ -1225,10 +1226,12 @@ struct dev_stream* dev_stream_create(struct cras_rstream* stream,
unsigned int dev_id,
const struct cras_audio_format* dev_fmt,
void* dev_ptr,
- struct timespec* cb_ts) {
+ struct timespec* cb_ts,
+ const struct timespec* sleep_interval_ts) {
struct dev_stream* out = static_cast<dev_stream*>(calloc(1, sizeof(*out)));
out->stream = stream;
init_cb_ts_ = *cb_ts;
+ sleep_interval_ts_ = *sleep_interval_ts;
return out;
}
@@ -1268,7 +1271,7 @@ void dev_stream_set_delay(const struct dev_stream* dev_stream,
void dev_stream_set_dev_rate(struct dev_stream* dev_stream,
unsigned int dev_rate,
double dev_rate_ratio,
- double master_rate_ratio,
+ double main_rate_ratio,
int coarse_rate_adjust) {}
void dev_stream_update_frames(const struct dev_stream* dev_stream) {}
@@ -1409,12 +1412,19 @@ int cras_device_monitor_set_device_mute_state(unsigned int dev_idx) {
cras_device_monitor_set_device_mute_state_called++;
return 0;
}
+int cras_device_monitor_error_close(unsigned int dev_idx) {
+ return 0;
+}
int cras_iodev_drop_frames_by_time(struct cras_iodev* iodev,
struct timespec ts) {
return 0;
}
+bool cras_iodev_is_on_internal_card(const struct cras_ionode* node) {
+ return 0;
+}
+
// From librt.
int clock_gettime(clockid_t clk_id, struct timespec* tp) {
*tp = clock_gettime_retspec;
diff --git a/cras/src/tests/bt_io_unittest.cc b/cras/src/tests/bt_io_unittest.cc
index ee013cf3..dd02652f 100644
--- a/cras/src/tests/bt_io_unittest.cc
+++ b/cras/src/tests/bt_io_unittest.cc
@@ -458,6 +458,10 @@ const char* cras_bt_device_object_path(const struct cras_bt_device* device) {
return "/fake/object/path";
}
+int cras_bt_device_get_stable_id(const struct cras_bt_device* device) {
+ return 123;
+}
+
int cras_bt_device_get_use_hardware_volume(struct cras_bt_device* device) {
return 1;
}
diff --git a/cras/src/tests/capture_rclient_unittest.cc b/cras/src/tests/capture_rclient_unittest.cc
index b749f1a5..446fddfa 100644
--- a/cras/src/tests/capture_rclient_unittest.cc
+++ b/cras/src/tests/capture_rclient_unittest.cc
@@ -270,4 +270,9 @@ bool cras_audio_format_valid(const struct cras_audio_format* fmt) {
return true;
}
+void detect_rtc_stream_pair(struct stream_list* list,
+ struct cras_rstream* stream) {
+ return;
+}
+
} // extern "C"
diff --git a/cras/src/tests/control_rclient_unittest.cc b/cras/src/tests/control_rclient_unittest.cc
index d6b63aab..63e3c8f0 100644
--- a/cras/src/tests/control_rclient_unittest.cc
+++ b/cras/src/tests/control_rclient_unittest.cc
@@ -967,4 +967,11 @@ struct packet_status_logger* cras_hfp_ag_get_wbs_logger() {
return NULL;
}
+void detect_rtc_stream_pair(struct stream_list* list,
+ struct cras_rstream* stream) {
+ return;
+}
+
+void cras_system_set_hotword_pause_at_suspend(bool pause) {}
+
} // extern "C"
diff --git a/cras/src/tests/cras_abi_unittest.cc b/cras/src/tests/cras_abi_unittest.cc
new file mode 100644
index 00000000..d566a9b7
--- /dev/null
+++ b/cras/src/tests/cras_abi_unittest.cc
@@ -0,0 +1,139 @@
+/* Copyright 2021 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "cras_client.c"
+#include "cras_client.h"
+
+inline int libcras_unsupported_func(struct libcras_client* client) {
+ CHECK_VERSION(client, INT_MAX);
+ return 0;
+}
+
+cras_stream_id_t cb_stream_id;
+uint8_t* cb_buf;
+unsigned int cb_frames;
+struct timespec cb_latency;
+void* cb_usr_arg;
+int get_stream_cb_called;
+struct timespec now;
+
+int get_stream_cb(struct libcras_stream_cb_data* data) {
+ get_stream_cb_called++;
+ EXPECT_NE((void*)NULL, data);
+ EXPECT_EQ(0, libcras_stream_cb_data_get_stream_id(data, &cb_stream_id));
+ EXPECT_EQ(0, libcras_stream_cb_data_get_buf(data, &cb_buf));
+ EXPECT_EQ(0, libcras_stream_cb_data_get_frames(data, &cb_frames));
+ EXPECT_EQ(0, libcras_stream_cb_data_get_latency(data, &cb_latency));
+ EXPECT_EQ(0, libcras_stream_cb_data_get_usr_arg(data, &cb_usr_arg));
+ return 0;
+}
+}
+
+namespace {
+class CrasAbiTestSuite : public testing::Test {
+ protected:
+ struct cras_audio_shm* InitShm(int frames) {
+ struct cras_audio_shm* shm =
+ static_cast<struct cras_audio_shm*>(calloc(1, sizeof(*shm)));
+ shm->header =
+ static_cast<cras_audio_shm_header*>(calloc(1, sizeof(*shm->header)));
+ cras_shm_set_frame_bytes(shm, 4);
+ uint32_t used_size = frames * 4;
+ cras_shm_set_used_size(shm, used_size);
+ shm->samples_info.length = used_size * 2;
+ memcpy(&shm->header->config, &shm->config, sizeof(shm->config));
+ return shm;
+ }
+
+ void DestroyShm(struct cras_audio_shm* shm) {
+ if (shm)
+ free(shm->header);
+ free(shm);
+ }
+
+ virtual void SetUp() { get_stream_cb_called = 0; }
+};
+
+TEST_F(CrasAbiTestSuite, CheckUnsupportedFunction) {
+ auto* client = libcras_client_create();
+ EXPECT_NE((void*)NULL, client);
+ EXPECT_EQ(-ENOSYS, libcras_unsupported_func(client));
+ libcras_client_destroy(client);
+}
+
+TEST_F(CrasAbiTestSuite, BasicStream) {
+ auto* client = libcras_client_create();
+ EXPECT_NE((void*)NULL, client);
+ auto* stream = libcras_stream_params_create();
+ EXPECT_NE((void*)NULL, stream);
+ /* Returns timeout because there is no real CRAS server in unittest. */
+ EXPECT_EQ(-ETIMEDOUT, libcras_client_connect_timeout(client, 0));
+ EXPECT_EQ(0, libcras_client_run_thread(client));
+ EXPECT_EQ(0, libcras_stream_params_set(stream, CRAS_STREAM_INPUT, 480, 480,
+ CRAS_STREAM_TYPE_DEFAULT,
+ CRAS_CLIENT_TYPE_TEST, 0, NULL, NULL,
+ NULL, 48000, SND_PCM_FORMAT_S16, 2));
+ cras_stream_id_t id;
+ /* Fails to add a stream because the stream callback is not set. */
+ EXPECT_EQ(-EINVAL, libcras_client_add_pinned_stream(client, 0, &id, stream));
+ /* Fails to set a stream volume because the stream is not added. */
+ EXPECT_EQ(-EINVAL, libcras_client_set_stream_volume(client, id, 1.0));
+ EXPECT_EQ(0, libcras_client_rm_stream(client, id));
+ EXPECT_EQ(0, libcras_client_stop(client));
+ libcras_stream_params_destroy(stream);
+ libcras_client_destroy(client);
+}
+
+TEST_F(CrasAbiTestSuite, StreamCallback) {
+ struct client_stream stream;
+ struct cras_stream_params params;
+ stream.id = 0x123;
+ stream.direction = CRAS_STREAM_INPUT;
+ stream.flags = 0;
+ stream.config = &params;
+ params.stream_cb = get_stream_cb;
+ params.cb_threshold = 480;
+ params.user_data = (void*)0x321;
+ stream.shm = InitShm(960);
+ stream.shm->header->write_offset[0] = 960 * 4;
+ stream.shm->header->write_buf_idx = 0;
+ stream.shm->header->read_offset[0] = 0;
+ stream.shm->header->read_buf_idx = 0;
+ now.tv_sec = 100;
+ now.tv_nsec = 0;
+ stream.shm->header->ts.tv_sec = 90;
+ stream.shm->header->ts.tv_nsec = 0;
+
+ handle_capture_data_ready(&stream, 480);
+
+ EXPECT_EQ(1, get_stream_cb_called);
+ EXPECT_EQ(stream.id, cb_stream_id);
+ EXPECT_EQ(cras_shm_get_write_buffer_base(stream.shm), cb_buf);
+ EXPECT_EQ(480, cb_frames);
+ EXPECT_EQ(10, cb_latency.tv_sec);
+ EXPECT_EQ(0, cb_latency.tv_nsec);
+ EXPECT_EQ((void*)0x321, cb_usr_arg);
+
+ DestroyShm(stream.shm);
+}
+
+} // namespace
+
+extern "C" {
+
+int clock_gettime(clockid_t clk_id, struct timespec* tp) {
+ *tp = now;
+ return 0;
+}
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ openlog(NULL, LOG_PERROR, LOG_USER);
+ return RUN_ALL_TESTS();
+}
diff --git a/cras/src/tests/dev_io_stubs.cc b/cras/src/tests/dev_io_stubs.cc
index b74162b8..d97dde50 100644
--- a/cras/src/tests/dev_io_stubs.cc
+++ b/cras/src/tests/dev_io_stubs.cc
@@ -151,6 +151,11 @@ void add_stream_to_dev(IodevPtr& dev, const StreamPtr& stream) {
static_cast<size_t>(dev->max_cb_level));
dev->largest_cb_level = std::max(stream->rstream->cb_threshold,
static_cast<size_t>(dev->max_cb_level));
+
+ if (stream->rstream->main_dev.dev_id == NO_DEVICE) {
+ stream->rstream->main_dev.dev_id = dev->info.idx;
+ stream->rstream->main_dev.dev_ptr = dev.get();
+ }
}
void fill_audio_format(cras_audio_format* format, unsigned int rate) {
diff --git a/cras/src/tests/dev_io_unittest.cc b/cras/src/tests/dev_io_unittest.cc
index 096e3ed3..2dbf344e 100644
--- a/cras/src/tests/dev_io_unittest.cc
+++ b/cras/src/tests/dev_io_unittest.cc
@@ -8,6 +8,7 @@
#include <time.h>
#include <memory>
+#include <unordered_map>
extern "C" {
#include "cras_iodev.h" // stubbed
@@ -29,6 +30,13 @@ struct audio_thread_event_log* atlog;
static float dev_stream_capture_software_gain_scaler_val;
static float input_data_get_software_gain_scaler_val;
static unsigned int dev_stream_capture_avail_ret = 480;
+struct set_dev_rate_data {
+ unsigned int dev_rate;
+ double dev_rate_ratio;
+ double main_rate_ratio;
+ int coarse_rate_adjust;
+};
+std::unordered_map<struct dev_stream*, set_dev_rate_data> set_dev_rate_map;
namespace {
@@ -39,6 +47,7 @@ class DevIoSuite : public testing::Test {
iodev_stub_reset();
rstream_stub_reset();
fill_audio_format(&format, 48000);
+ set_dev_rate_map.clear();
stream = create_stream(1, 1, CRAS_STREAM_INPUT, cb_threshold, &format);
}
@@ -70,6 +79,7 @@ TEST_F(DevIoSuite, SendCapturedFails) {
TEST_F(DevIoSuite, CaptureGain) {
struct open_dev* dev_list = NULL;
+ struct open_dev* odev_list = NULL;
struct timespec ts;
DevicePtr dev = create_device(CRAS_STREAM_INPUT, cb_threshold, &format,
CRAS_NODE_TYPE_MIC);
@@ -82,20 +92,80 @@ TEST_F(DevIoSuite, CaptureGain) {
/* The applied scaler gain should match what is reported by input_data. */
dev->dev->active_node->ui_gain_scaler = 1.0f;
input_data_get_software_gain_scaler_val = 1.0f;
- dev_io_capture(&dev_list);
+ dev_io_capture(&dev_list, &odev_list);
EXPECT_EQ(1.0f, dev_stream_capture_software_gain_scaler_val);
input_data_get_software_gain_scaler_val = 0.99f;
- dev_io_capture(&dev_list);
+ dev_io_capture(&dev_list, &odev_list);
EXPECT_EQ(0.99f, dev_stream_capture_software_gain_scaler_val);
dev->dev->active_node->ui_gain_scaler = 0.6f;
input_data_get_software_gain_scaler_val = 0.7f;
- dev_io_capture(&dev_list);
+ dev_io_capture(&dev_list, &odev_list);
EXPECT_FLOAT_EQ(0.42f, dev_stream_capture_software_gain_scaler_val);
}
/*
+ * When input and output devices are on the internal sound card,
+ * and their device rates are the same, use the estimated rate
+ * on the output device as the estimated rate of input device.
+ */
+TEST_F(DevIoSuite, CopyOutputEstimatedRate) {
+ struct open_dev* idev_list = NULL;
+ struct open_dev* odev_list = NULL;
+ struct timespec ts;
+ DevicePtr out_dev = create_device(CRAS_STREAM_OUTPUT, cb_threshold, &format,
+ CRAS_NODE_TYPE_INTERNAL_SPEAKER);
+ DevicePtr in_dev = create_device(CRAS_STREAM_INPUT, cb_threshold, &format,
+ CRAS_NODE_TYPE_MIC);
+
+ in_dev->dev->state = CRAS_IODEV_STATE_NORMAL_RUN;
+ iodev_stub_frames_queued(in_dev->dev.get(), 20, ts);
+ DL_APPEND(idev_list, in_dev->odev.get());
+ add_stream_to_dev(in_dev->dev, stream);
+ DL_APPEND(odev_list, out_dev->odev.get());
+ iodev_stub_on_internal_card(out_dev->dev->active_node, 1);
+ iodev_stub_on_internal_card(in_dev->dev->active_node, 1);
+
+ iodev_stub_est_rate_ratio(in_dev->dev.get(), 0.8f);
+ iodev_stub_est_rate_ratio(out_dev->dev.get(), 1.2f);
+
+ dev_io_capture(&idev_list, &odev_list);
+
+ EXPECT_FLOAT_EQ(1.2f, set_dev_rate_map[stream->dstream.get()].dev_rate_ratio);
+}
+
+/*
+ * When input and output devices are not both on the internal sound card,
+ * estimated rates are independent.
+ */
+TEST_F(DevIoSuite, InputOutputIndependentEstimatedRate) {
+ struct open_dev* idev_list = NULL;
+ struct open_dev* odev_list = NULL;
+ struct timespec ts;
+ DevicePtr out_dev = create_device(CRAS_STREAM_OUTPUT, cb_threshold, &format,
+ CRAS_NODE_TYPE_INTERNAL_SPEAKER);
+ DevicePtr in_dev = create_device(CRAS_STREAM_INPUT, cb_threshold, &format,
+ CRAS_NODE_TYPE_USB);
+
+ in_dev->dev->state = CRAS_IODEV_STATE_NORMAL_RUN;
+ iodev_stub_frames_queued(in_dev->dev.get(), 20, ts);
+ DL_APPEND(idev_list, in_dev->odev.get());
+ add_stream_to_dev(in_dev->dev, stream);
+ DL_APPEND(odev_list, out_dev->odev.get());
+ iodev_stub_on_internal_card(out_dev->dev->active_node, 1);
+ iodev_stub_on_internal_card(in_dev->dev->active_node, 0);
+
+ iodev_stub_est_rate_ratio(in_dev->dev.get(), 0.8f);
+ iodev_stub_est_rate_ratio(out_dev->dev.get(), 1.2f);
+ iodev_stub_update_rate(in_dev->dev.get(), 1);
+
+ dev_io_capture(&idev_list, &odev_list);
+
+ EXPECT_FLOAT_EQ(0.8f, set_dev_rate_map[stream->dstream.get()].dev_rate_ratio);
+}
+
+/*
* If any hw_level is larger than 1.5 * largest_cb_level and
* DROP_FRAMES_THRESHOLD_MS, reset all input devices.
*/
@@ -332,8 +402,16 @@ int dev_stream_mix(struct dev_stream* dev_stream,
void dev_stream_set_dev_rate(struct dev_stream* dev_stream,
unsigned int dev_rate,
double dev_rate_ratio,
- double master_rate_ratio,
- int coarse_rate_adjust) {}
+ double main_rate_ratio,
+ int coarse_rate_adjust) {
+ set_dev_rate_data new_data;
+ new_data.dev_rate = dev_rate;
+ new_data.dev_rate_ratio = dev_rate_ratio;
+ new_data.main_rate_ratio = main_rate_ratio;
+ new_data.coarse_rate_adjust = coarse_rate_adjust;
+
+ set_dev_rate_map[dev_stream] = new_data;
+}
int dev_stream_capture_update_rstream(struct dev_stream* dev_stream) {
return 0;
}
@@ -373,7 +451,11 @@ struct dev_stream* dev_stream_create(struct cras_rstream* stream,
unsigned int dev_id,
const struct cras_audio_format* dev_fmt,
void* dev_ptr,
- struct timespec* cb_ts) {
+ struct timespec* cb_ts,
+ const struct timespec* sleep_interval_ts) {
+ return 0;
+}
+int cras_device_monitor_error_close(unsigned int dev_idx) {
return 0;
}
} // extern "C"
diff --git a/cras/src/tests/dev_stream_unittest.cc b/cras/src/tests/dev_stream_unittest.cc
index 640ca932..700376fb 100644
--- a/cras/src/tests/dev_stream_unittest.cc
+++ b/cras/src/tests/dev_stream_unittest.cc
@@ -334,7 +334,7 @@ TEST_F(CreateSuite, CreateSRC44to48) {
out_fmt.frame_rate = 48000; // Output from converter is device rate.
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
dev_stream =
- dev_stream_create(&rstream_, 0, &fmt_s16le_48, (void*)0x55, &cb_ts);
+ dev_stream_create(&rstream_, 0, &fmt_s16le_48, (void*)0x55, &cb_ts, NULL);
EXPECT_EQ(1, config_format_converter_called);
EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
// Converter tmp and output buffers are large enough for device output.
@@ -346,6 +346,24 @@ TEST_F(CreateSuite, CreateSRC44to48) {
dev_stream_destroy(dev_stream);
}
+TEST_F(CreateSuite, CreateOutputWithSchedule) {
+ struct dev_stream* dev_stream;
+ unsigned int dev_id = 9;
+ // init_cb_ts and non-null init_sleep_ts will be used.
+ struct timespec init_cb_ts = {1, 2};
+ struct timespec init_sleep_ts = {3, 4};
+
+ rstream_.direction = CRAS_STREAM_OUTPUT;
+ dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_48, (void*)0x55,
+ &init_cb_ts, &init_sleep_ts);
+
+ EXPECT_EQ(init_cb_ts.tv_sec, rstream_.next_cb_ts.tv_sec);
+ EXPECT_EQ(init_cb_ts.tv_nsec, rstream_.next_cb_ts.tv_nsec);
+ EXPECT_EQ(init_sleep_ts.tv_sec, rstream_.sleep_interval_ts.tv_sec);
+ EXPECT_EQ(init_sleep_ts.tv_nsec, rstream_.sleep_interval_ts.tv_nsec);
+ dev_stream_destroy(dev_stream);
+}
+
TEST_F(CreateSuite, CreateSRC44from48Input) {
struct dev_stream* dev_stream;
struct cras_audio_format processed_fmt = fmt_s16le_48;
@@ -358,7 +376,7 @@ TEST_F(CreateSuite, CreateSRC44from48Input) {
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
cras_rstream_post_processing_format_val = &processed_fmt;
dev_stream =
- dev_stream_create(&rstream_, 0, &fmt_s16le_48, (void*)0x55, &cb_ts);
+ dev_stream_create(&rstream_, 0, &fmt_s16le_48, (void*)0x55, &cb_ts, NULL);
EXPECT_EQ(1, config_format_converter_called);
EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
// Converter tmp and output buffers are large enough for device input.
@@ -378,8 +396,8 @@ TEST_F(CreateSuite, CreateSRC48to44) {
in_fmt.frame_rate = 48000; // Stream rate.
out_fmt.frame_rate = 44100; // Device rate.
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
- dev_stream =
- dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void*)0x55, &cb_ts);
+ dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void*)0x55,
+ &cb_ts, NULL);
EXPECT_EQ(1, config_format_converter_called);
EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
// Converter tmp and output buffers are large enough for stream input.
@@ -396,8 +414,8 @@ TEST_F(CreateSuite, CreateSRC48from44Input) {
in_fmt.frame_rate = 44100; // Device rate.
out_fmt.frame_rate = 48000; // Stream rate.
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
- dev_stream =
- dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void*)0x55, &cb_ts);
+ dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void*)0x55,
+ &cb_ts, NULL);
EXPECT_EQ(1, config_format_converter_called);
EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
// Converter tmp and output buffers are large enough for stream output.
@@ -414,7 +432,7 @@ TEST_F(CreateSuite, CreateSRC8to48) {
out_fmt.frame_rate = 48000; // Device rate.
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
dev_stream =
- dev_stream_create(&rstream_, 0, &fmt_s16le_48, (void*)0x55, &cb_ts);
+ dev_stream_create(&rstream_, 0, &fmt_s16le_48, (void*)0x55, &cb_ts, NULL);
EXPECT_EQ(1, config_format_converter_called);
EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
// Converter tmp and output buffers are large enough for device output.
@@ -435,7 +453,7 @@ TEST_F(CreateSuite, CreateSRC8from48Input) {
out_fmt.frame_rate = 8000; // Stream rate.
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
dev_stream =
- dev_stream_create(&rstream_, 0, &fmt_s16le_48, (void*)0x55, &cb_ts);
+ dev_stream_create(&rstream_, 0, &fmt_s16le_48, (void*)0x55, &cb_ts, NULL);
EXPECT_EQ(1, config_format_converter_called);
EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
// Converter tmp and output buffers are large enough for device input.
@@ -455,7 +473,7 @@ TEST_F(CreateSuite, CreateSRC48to8) {
out_fmt.frame_rate = 8000; // Device rate.
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
dev_stream =
- dev_stream_create(&rstream_, 0, &fmt_s16le_8, (void*)0x55, &cb_ts);
+ dev_stream_create(&rstream_, 0, &fmt_s16le_8, (void*)0x55, &cb_ts, NULL);
EXPECT_EQ(1, config_format_converter_called);
EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
// Converter tmp and output buffers are large enough for stream input.
@@ -473,7 +491,7 @@ TEST_F(CreateSuite, CreateSRC48from8Input) {
out_fmt.frame_rate = 48000; // Stream rate.
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
dev_stream =
- dev_stream_create(&rstream_, 0, &fmt_s16le_8, (void*)0x55, &cb_ts);
+ dev_stream_create(&rstream_, 0, &fmt_s16le_8, (void*)0x55, &cb_ts, NULL);
EXPECT_EQ(1, config_format_converter_called);
EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
// Converter tmp and output buffers are large enough for stream output.
@@ -490,8 +508,8 @@ TEST_F(CreateSuite, CreateSRC48MonoFrom44StereoInput) {
in_fmt.frame_rate = 44100; // Device rate.
out_fmt.frame_rate = 48000; // Stream rate.
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
- dev_stream =
- dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void*)0x55, &cb_ts);
+ dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void*)0x55,
+ &cb_ts, NULL);
EXPECT_EQ(1, config_format_converter_called);
EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
// Converter tmp and output buffers are large enough for stream output.
@@ -510,8 +528,8 @@ TEST_F(CreateSuite, CaptureAvailConvBufHasSamples) {
rstream_.format = fmt_s16le_48;
rstream_.direction = CRAS_STREAM_INPUT;
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
- dev_stream =
- dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void*)0x55, &cb_ts);
+ dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void*)0x55,
+ &cb_ts, NULL);
EXPECT_EQ(1, config_format_converter_called);
EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
EXPECT_LE(
@@ -529,16 +547,16 @@ TEST_F(CreateSuite, CaptureAvailConvBufHasSamples) {
dev_stream_destroy(dev_stream);
}
-TEST_F(CreateSuite, SetDevRateNotMasterDev) {
+TEST_F(CreateSuite, SetDevRateNotMainDev) {
struct dev_stream* dev_stream;
unsigned int dev_id = 9;
rstream_.format = fmt_s16le_48;
rstream_.direction = CRAS_STREAM_INPUT;
- rstream_.master_dev.dev_id = 4;
+ rstream_.main_dev.dev_id = 4;
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
- (void*)0x55, &cb_ts);
+ (void*)0x55, &cb_ts, NULL);
dev_stream_set_dev_rate(dev_stream, 44100, 1.01, 1.0, 0);
EXPECT_EQ(1, cras_fmt_conv_set_linear_resample_rates_called);
@@ -557,17 +575,17 @@ TEST_F(CreateSuite, SetDevRateNotMasterDev) {
dev_stream_destroy(dev_stream);
}
-TEST_F(CreateSuite, SetDevRateMasterDev) {
+TEST_F(CreateSuite, SetDevRateMainDev) {
struct dev_stream* dev_stream;
unsigned int dev_id = 9;
unsigned int expected_ts_nsec;
rstream_.format = fmt_s16le_48;
rstream_.direction = CRAS_STREAM_INPUT;
- rstream_.master_dev.dev_id = dev_id;
+ rstream_.main_dev.dev_id = dev_id;
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
- (void*)0x55, &cb_ts);
+ (void*)0x55, &cb_ts, NULL);
dev_stream_set_dev_rate(dev_stream, 44100, 1.01, 1.0, 0);
EXPECT_EQ(1, cras_fmt_conv_set_linear_resample_rates_called);
@@ -661,7 +679,7 @@ TEST_F(CreateSuite, DevStreamFlushAudioMessages) {
unsigned int dev_id = 9;
dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
- (void*)0x55, &cb_ts);
+ (void*)0x55, &cb_ts, NULL);
dev_stream_flush_old_audio_messages(dev_stream);
EXPECT_EQ(1, cras_rstream_flush_old_audio_messages_called);
@@ -673,7 +691,7 @@ TEST_F(CreateSuite, DevStreamIsPending) {
unsigned int dev_id = 9;
dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
- (void*)0x55, &cb_ts);
+ (void*)0x55, &cb_ts, NULL);
// dev_stream_is_pending_reply is only a wrapper.
cras_rstream_is_pending_reply_ret = 0;
@@ -694,7 +712,7 @@ TEST_F(CreateSuite, StreamCanSend) {
rstream_.direction = CRAS_STREAM_INPUT;
dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
- (void*)0x55, &cb_ts);
+ (void*)0x55, &cb_ts, NULL);
// Assume there is a next_cb_ts on rstream.
rstream_.next_cb_ts.tv_sec = 1;
@@ -791,7 +809,7 @@ TEST_F(CreateSuite, StreamCanSendBulkAudio) {
rstream_.direction = CRAS_STREAM_INPUT;
rstream_.flags |= BULK_AUDIO_OK;
dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
- (void*)0x55, &cb_ts);
+ (void*)0x55, &cb_ts, NULL);
// Assume there is a next_cb_ts on rstream.
rstream_.next_cb_ts.tv_sec = 1;
@@ -864,7 +882,7 @@ TEST_F(CreateSuite, TriggerOnlyStreamSendOnlyOnce) {
rstream_.direction = CRAS_STREAM_INPUT;
dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
- (void*)0x55, &cb_ts);
+ (void*)0x55, &cb_ts, NULL);
dev_stream->stream->flags = TRIGGER_ONLY;
dev_stream->stream->triggered = 0;
@@ -896,7 +914,7 @@ TEST_F(CreateSuite, InputDevStreamWakeTimeByNextCbTs) {
rstream_.direction = CRAS_STREAM_INPUT;
dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
- (void*)0x55, &cb_ts);
+ (void*)0x55, &cb_ts, NULL);
// Assume there is a next_cb_ts on rstream.
rstream_.next_cb_ts.tv_sec = 1;
@@ -929,8 +947,8 @@ TEST_F(CreateSuite, InputDevStreamWakeTimeByDevice) {
int needed_frames_from_device = 0;
rstream_.direction = CRAS_STREAM_INPUT;
- dev_stream =
- dev_stream_create(&rstream_, dev_id, &fmt_s16le_48, (void*)0x55, &cb_ts);
+ dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_48, (void*)0x55,
+ &cb_ts, NULL);
// Assume there is a next_cb_ts on rstream, that is, 1.005 seconds.
rstream_.next_cb_ts.tv_sec = 1;
@@ -994,7 +1012,7 @@ TEST_F(CreateSuite, UpdateNextWakeTime) {
rstream_.direction = CRAS_STREAM_OUTPUT;
dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
- (void*)0x55, &cb_ts);
+ (void*)0x55, &cb_ts, NULL);
// Case 1: The new next_cb_ts is greater than now. Do not need to reschedule.
rstream_.next_cb_ts.tv_sec = 2;
diff --git a/cras/src/tests/fmt_conv_ops_unittest.cc b/cras/src/tests/fmt_conv_ops_unittest.cc
index ebe8b65d..0baf37b1 100644
--- a/cras/src/tests/fmt_conv_ops_unittest.cc
+++ b/cras/src/tests/fmt_conv_ops_unittest.cc
@@ -418,6 +418,39 @@ TEST(FormatConverterOpsTest, StereoTo51S16LECenter) {
}
}
+// Test Quad to 5.1 conversion. S16_LE.
+TEST(FormatConverterOpsTest, QuadTo51S16LE) {
+ const size_t frames = 4096;
+ const size_t in_ch = 4;
+ const size_t out_ch = 6;
+ const unsigned int fl_quad = 0;
+ const unsigned int fr_quad = 1;
+ const unsigned int rl_quad = 2;
+ const unsigned int rr_quad = 3;
+
+ const unsigned int fl_51 = 0;
+ const unsigned int fr_51 = 1;
+ const unsigned int center_51 = 2;
+ const unsigned int lfe_51 = 3;
+ const unsigned int rl_51 = 4;
+ const unsigned int rr_51 = 5;
+
+ S16LEPtr src = CreateS16LE(frames * in_ch);
+ S16LEPtr dst = CreateS16LE(frames * out_ch);
+
+ size_t ret = s16_quad_to_51(fl_51, fr_51, rl_51, rr_51, (uint8_t*)src.get(),
+ frames, (uint8_t*)dst.get());
+ EXPECT_EQ(ret, frames);
+ for (size_t i = 0; i < frames; ++i) {
+ EXPECT_EQ(0, dst[i * 6 + center_51]);
+ EXPECT_EQ(0, dst[i * 6 + lfe_51]);
+ EXPECT_EQ(src[i * 4 + fl_quad], dst[i * 6 + fl_51]);
+ EXPECT_EQ(src[i * 4 + fr_quad], dst[i * 6 + fr_51]);
+ EXPECT_EQ(src[i * 4 + rl_quad], dst[i * 6 + rl_51]);
+ EXPECT_EQ(src[i * 4 + rr_quad], dst[i * 6 + rr_51]);
+ }
+}
+
// Test Stereo to 5.1 conversion. S16_LE, LeftRight.
TEST(FormatConverterOpsTest, StereoTo51S16LELeftRight) {
const size_t frames = 4096;
diff --git a/cras/src/tests/hfp_alsa_iodev_unittest.cc b/cras/src/tests/hfp_alsa_iodev_unittest.cc
index c5bd4e9a..8756c201 100644
--- a/cras/src/tests/hfp_alsa_iodev_unittest.cc
+++ b/cras/src/tests/hfp_alsa_iodev_unittest.cc
@@ -259,7 +259,7 @@ TEST_F(HfpAlsaIodev, ConfigureDev) {
hfp_alsa_io->aio->format->channel_layout[i]);
EXPECT_EQ(1, fake_configure_dev_called);
- EXPECT_EQ(0, hfp_set_call_status_called);
+ EXPECT_EQ(1, hfp_set_call_status_called);
EXPECT_EQ(buf_size, iodev->buffer_size);
hfp_alsa_iodev_destroy(iodev);
@@ -273,7 +273,7 @@ TEST_F(HfpAlsaIodev, CloseDev) {
CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY);
iodev->close_dev(iodev);
- EXPECT_EQ(0, hfp_set_call_status_called);
+ EXPECT_EQ(1, hfp_set_call_status_called);
EXPECT_EQ(1, cras_iodev_free_format_called);
EXPECT_EQ(1, fake_close_dev_called);
@@ -507,6 +507,10 @@ const char* cras_bt_device_object_path(const struct cras_bt_device* device) {
return "/fake/object/path";
}
+int cras_bt_device_get_stable_id(const struct cras_bt_device* device) {
+ return 123;
+}
+
void cras_iodev_free_resources(struct cras_iodev* iodev) {
cras_iodev_free_resources_called++;
}
diff --git a/cras/src/tests/hfp_iodev_unittest.cc b/cras/src/tests/hfp_iodev_unittest.cc
index 18262bf9..1275ef2c 100644
--- a/cras/src/tests/hfp_iodev_unittest.cc
+++ b/cras/src/tests/hfp_iodev_unittest.cc
@@ -285,6 +285,10 @@ const char* cras_bt_device_object_path(const struct cras_bt_device* device) {
return "/fake/object/path";
}
+int cras_bt_device_get_stable_id(const struct cras_bt_device* device) {
+ return 123;
+}
+
// From cras_hfp_info
int hfp_info_add_iodev(struct hfp_info* info,
enum CRAS_STREAM_DIRECTION direction,
diff --git a/cras/src/tests/iodev_list_unittest.cc b/cras/src/tests/iodev_list_unittest.cc
index 272537fc..8c71214a 100644
--- a/cras/src/tests/iodev_list_unittest.cc
+++ b/cras/src/tests/iodev_list_unittest.cc
@@ -95,6 +95,7 @@ static struct cras_rstream* audio_thread_disconnect_stream_stream;
static int audio_thread_disconnect_stream_called;
static struct cras_iodev fake_sco_in_dev, fake_sco_out_dev;
static struct cras_ionode fake_sco_in_node, fake_sco_out_node;
+static int server_state_hotword_pause_at_suspend;
int dev_idx_in_vector(std::vector<unsigned int> v, unsigned int idx) {
return std::find(v.begin(), v.end(), idx) != v.end();
@@ -238,6 +239,7 @@ class IoDevTestSuite : public testing::Test {
mock_empty_iodev[1].state = CRAS_IODEV_STATE_CLOSE;
mock_empty_iodev[1].update_active_node = update_active_node;
mock_hotword_iodev.update_active_node = update_active_node;
+ server_state_hotword_pause_at_suspend = 0;
}
virtual void TearDown() {
@@ -1942,6 +1944,105 @@ TEST_F(IoDevTestSuite, GetSCOPCMIodevs) {
cras_iodev_list_deinit();
}
+TEST_F(IoDevTestSuite, HotwordStreamsPausedAtSystemSuspend) {
+ struct cras_rstream rstream;
+ struct cras_rstream* stream_list = NULL;
+ cras_iodev_list_init();
+
+ node1.type = CRAS_NODE_TYPE_HOTWORD;
+ d1_.direction = CRAS_STREAM_INPUT;
+ EXPECT_EQ(0, cras_iodev_list_add_input(&d1_));
+
+ d1_.format = &fmt_;
+
+ memset(&rstream, 0, sizeof(rstream));
+ rstream.is_pinned = 1;
+ rstream.pinned_dev_idx = d1_.info.idx;
+ rstream.flags = HOTWORD_STREAM;
+
+ /* Add a hotword stream. */
+ EXPECT_EQ(0, stream_add_cb(&rstream));
+ EXPECT_EQ(1, audio_thread_add_stream_called);
+ EXPECT_EQ(&d1_, audio_thread_add_stream_dev);
+ EXPECT_EQ(&rstream, audio_thread_add_stream_stream);
+
+ DL_APPEND(stream_list, &rstream);
+ stream_list_get_ret = stream_list;
+
+ server_state_hotword_pause_at_suspend = 1;
+
+ /* Trigger system suspend. Verify hotword stream is moved to empty dev. */
+ observer_ops->suspend_changed(NULL, 1);
+ EXPECT_EQ(1, audio_thread_disconnect_stream_called);
+ EXPECT_EQ(&rstream, audio_thread_disconnect_stream_stream);
+ EXPECT_EQ(&d1_, audio_thread_disconnect_stream_dev);
+ EXPECT_EQ(2, audio_thread_add_stream_called);
+ EXPECT_EQ(&rstream, audio_thread_add_stream_stream);
+ EXPECT_EQ(&mock_hotword_iodev, audio_thread_add_stream_dev);
+
+ /* Trigger system resume. Verify hotword stream is moved to real dev.*/
+ observer_ops->suspend_changed(NULL, 0);
+ EXPECT_EQ(2, audio_thread_disconnect_stream_called);
+ EXPECT_EQ(&rstream, audio_thread_disconnect_stream_stream);
+ EXPECT_EQ(&mock_hotword_iodev, audio_thread_disconnect_stream_dev);
+ EXPECT_EQ(3, audio_thread_add_stream_called);
+ EXPECT_EQ(&rstream, audio_thread_add_stream_stream);
+ EXPECT_EQ(&d1_, audio_thread_add_stream_dev);
+
+ server_state_hotword_pause_at_suspend = 0;
+ audio_thread_disconnect_stream_called = 0;
+ audio_thread_add_stream_called = 0;
+
+ /* Trigger system suspend. Verify hotword stream is not touched. */
+ observer_ops->suspend_changed(NULL, 1);
+ EXPECT_EQ(0, audio_thread_disconnect_stream_called);
+ EXPECT_EQ(0, audio_thread_add_stream_called);
+
+ /* Trigger system resume. Verify hotword stream is not touched.*/
+ observer_ops->suspend_changed(NULL, 0);
+ EXPECT_EQ(0, audio_thread_disconnect_stream_called);
+ EXPECT_EQ(0, audio_thread_add_stream_called);
+
+ cras_iodev_list_deinit();
+}
+
+TEST_F(IoDevTestSuite, SetNoiseCancellation) {
+ struct cras_rstream rstream;
+ struct cras_rstream* stream_list = NULL;
+ int rc;
+
+ memset(&rstream, 0, sizeof(rstream));
+
+ cras_iodev_list_init();
+
+ d1_.direction = CRAS_STREAM_INPUT;
+ rc = cras_iodev_list_add_input(&d1_);
+ ASSERT_EQ(0, rc);
+
+ d1_.format = &fmt_;
+
+ rstream.direction = CRAS_STREAM_INPUT;
+
+ audio_thread_add_open_dev_called = 0;
+ audio_thread_rm_open_dev_called = 0;
+ cras_iodev_list_add_active_node(CRAS_STREAM_INPUT,
+ cras_make_node_id(d1_.info.idx, 1));
+ DL_APPEND(stream_list, &rstream);
+ stream_add_cb(&rstream);
+ stream_list_get_ret = stream_list;
+ EXPECT_EQ(1, audio_thread_add_stream_called);
+ EXPECT_EQ(1, audio_thread_add_open_dev_called);
+
+ // reset_for_noise_cancellation causes device suspend & resume
+ // While suspending d1_: rm d1_, open fallback
+ // While resuming d1_: rm fallback, open d1_
+ cras_iodev_list_reset_for_noise_cancellation();
+ EXPECT_EQ(3, audio_thread_add_open_dev_called);
+ EXPECT_EQ(2, audio_thread_rm_open_dev_called);
+
+ cras_iodev_list_deinit();
+}
+
} // namespace
int main(int argc, char** argv) {
@@ -1964,6 +2065,10 @@ int cras_system_get_mute() {
return system_get_mute_return;
}
+bool cras_system_get_noise_cancellation_enabled() {
+ return false;
+}
+
struct audio_thread* audio_thread_create() {
return &thread;
}
@@ -2103,6 +2208,10 @@ void cras_iodev_set_node_plugged(struct cras_ionode* node, int plugged) {
set_node_plugged_called++;
}
+bool cras_iodev_support_noise_cancellation(const struct cras_iodev* iodev) {
+ return true;
+}
+
int cras_iodev_start_volume_ramp(struct cras_iodev* odev,
unsigned int old_volume,
unsigned int new_volume) {
@@ -2246,4 +2355,8 @@ int clock_gettime(clockid_t clk_id, struct timespec* tp) {
return 0;
}
+bool cras_system_get_hotword_pause_at_suspend() {
+ return !!server_state_hotword_pause_at_suspend;
+}
+
} // extern "C"
diff --git a/cras/src/tests/iodev_stub.cc b/cras/src/tests/iodev_stub.cc
index 3dbb61d1..2e84faac 100644
--- a/cras/src/tests/iodev_stub.cc
+++ b/cras/src/tests/iodev_stub.cc
@@ -21,12 +21,30 @@ struct cb_data {
std::unordered_map<cras_iodev*, cb_data> frames_queued_map;
std::unordered_map<cras_iodev*, cb_data> valid_frames_map;
std::unordered_map<cras_iodev*, timespec> drop_time_map;
+std::unordered_map<const cras_iodev*, double> est_rate_ratio_map;
+std::unordered_map<const cras_iodev*, int> update_rate_map;
+std::unordered_map<const cras_ionode*, int> on_internal_card_map;
} // namespace
void iodev_stub_reset() {
frames_queued_map.clear();
valid_frames_map.clear();
drop_time_map.clear();
+ est_rate_ratio_map.clear();
+ update_rate_map.clear();
+ on_internal_card_map.clear();
+}
+
+void iodev_stub_est_rate_ratio(cras_iodev* iodev, double ratio) {
+ est_rate_ratio_map.insert({iodev, ratio});
+}
+
+void iodev_stub_update_rate(cras_iodev* iodev, int data) {
+ update_rate_map.insert({iodev, data});
+}
+
+void iodev_stub_on_internal_card(cras_ionode* node, int data) {
+ on_internal_card_map.insert({node, data});
}
void iodev_stub_frames_queued(cras_iodev* iodev, int ret, timespec ts) {
@@ -67,7 +85,11 @@ int cras_iodev_get_valid_frames(struct cras_iodev* iodev,
}
double cras_iodev_get_est_rate_ratio(const struct cras_iodev* iodev) {
- return 1.0;
+ auto elem = est_rate_ratio_map.find(iodev);
+ if (elem != est_rate_ratio_map.end()) {
+ return elem->second;
+ }
+ return 1.0f;
}
int cras_iodev_get_dsp_delay(const struct cras_iodev* iodev) {
@@ -93,6 +115,10 @@ struct dev_stream* cras_iodev_rm_stream(struct cras_iodev* iodev,
int cras_iodev_update_rate(struct cras_iodev* iodev,
unsigned int level,
struct timespec* level_tstamp) {
+ auto elem = update_rate_map.find(iodev);
+ if (elem != update_rate_map.end()) {
+ return elem->second;
+ }
return 0;
}
@@ -188,4 +214,12 @@ int cras_iodev_drop_frames_by_time(struct cras_iodev* iodev,
drop_time_map.insert({iodev, ts});
return 0;
}
+
+bool cras_iodev_is_on_internal_card(const struct cras_ionode* node) {
+ auto elem = on_internal_card_map.find(node);
+ if (elem != on_internal_card_map.end()) {
+ return elem->second;
+ }
+ return 1;
+}
} // extern "C"
diff --git a/cras/src/tests/iodev_stub.h b/cras/src/tests/iodev_stub.h
index dde1b9f4..e8016dd3 100644
--- a/cras/src/tests/iodev_stub.h
+++ b/cras/src/tests/iodev_stub.h
@@ -10,6 +10,12 @@
void iodev_stub_reset();
+void iodev_stub_est_rate_ratio(cras_iodev* iodev, double ratio);
+
+void iodev_stub_update_rate(cras_iodev* iodev, int data);
+
+void iodev_stub_on_internal_card(cras_ionode* node, int data);
+
void iodev_stub_frames_queued(cras_iodev* iodev, int ret, timespec ts);
void iodev_stub_valid_frames(cras_iodev* iodev, int ret, timespec ts);
diff --git a/cras/src/tests/iodev_unittest.cc b/cras/src/tests/iodev_unittest.cc
index 21dc4d57..24b2b38d 100644
--- a/cras/src/tests/iodev_unittest.cc
+++ b/cras/src/tests/iodev_unittest.cc
@@ -2404,6 +2404,20 @@ TEST(IoDev, DeviceOverrun) {
EXPECT_EQ(1, cras_audio_thread_event_dev_overrun_called);
}
+TEST(IoDev, OnInternalCard) {
+ static struct cras_ionode node;
+ node.type = CRAS_NODE_TYPE_INTERNAL_SPEAKER;
+ EXPECT_EQ(1, cras_iodev_is_on_internal_card(&node));
+ node.type = CRAS_NODE_TYPE_HEADPHONE;
+ EXPECT_EQ(1, cras_iodev_is_on_internal_card(&node));
+ node.type = CRAS_NODE_TYPE_MIC;
+ EXPECT_EQ(1, cras_iodev_is_on_internal_card(&node));
+ node.type = CRAS_NODE_TYPE_USB;
+ EXPECT_EQ(0, cras_iodev_is_on_internal_card(&node));
+ node.type = CRAS_NODE_TYPE_BLUETOOTH;
+ EXPECT_EQ(0, cras_iodev_is_on_internal_card(&node));
+}
+
extern "C" {
struct main_thread_event_log* main_log;
diff --git a/cras/src/tests/playback_rclient_unittest.cc b/cras/src/tests/playback_rclient_unittest.cc
index 75cbe552..31ceda74 100644
--- a/cras/src/tests/playback_rclient_unittest.cc
+++ b/cras/src/tests/playback_rclient_unittest.cc
@@ -300,4 +300,9 @@ bool cras_audio_format_valid(const struct cras_audio_format* fmt) {
return audio_format_valid;
}
+void detect_rtc_stream_pair(struct stream_list* list,
+ struct cras_rstream* stream) {
+ return;
+}
+
} // extern "C"
diff --git a/cras/src/tests/rstream_unittest.cc b/cras/src/tests/rstream_unittest.cc
index 593c805d..d8dae24c 100644
--- a/cras/src/tests/rstream_unittest.cc
+++ b/cras/src/tests/rstream_unittest.cc
@@ -32,6 +32,7 @@ class RstreamTestSuite : public testing::Test {
config_.stream_id = 555;
config_.stream_type = CRAS_STREAM_TYPE_DEFAULT;
+ config_.client_type = CRAS_CLIENT_TYPE_UNKNOWN;
config_.direction = CRAS_STREAM_OUTPUT;
config_.dev_idx = NO_DEVICE;
config_.flags = 0;
diff --git a/cras/src/tests/server_metrics_unittest.cc b/cras/src/tests/server_metrics_unittest.cc
index fe80e26f..e23906ec 100644
--- a/cras/src/tests/server_metrics_unittest.cc
+++ b/cras/src/tests/server_metrics_unittest.cc
@@ -132,20 +132,6 @@ TEST(ServerMetricsTestSuite, SetMetricHighestHardwareLevel) {
EXPECT_EQ(sent_msgs[0].data.value, hw_level);
}
-TEST(ServerMetricsTestSuite, SetMetricsLongestFetchDelay) {
- ResetStubData();
- unsigned int delay = 100;
-
- cras_server_metrics_longest_fetch_delay(delay);
-
- EXPECT_EQ(sent_msgs.size(), 1);
- EXPECT_EQ(sent_msgs[0].header.type, CRAS_MAIN_METRICS);
- EXPECT_EQ(sent_msgs[0].header.length,
- sizeof(struct cras_server_metrics_message));
- EXPECT_EQ(sent_msgs[0].metrics_type, LONGEST_FETCH_DELAY);
- EXPECT_EQ(sent_msgs[0].data.value, delay);
-}
-
TEST(ServerMetricsTestSuite, SetMetricsNumUnderruns) {
ResetStubData();
unsigned int underrun = 10;
@@ -283,13 +269,18 @@ TEST(ServerMetricsTestSuite, SetMetricsStreamDestroy) {
stream.num_missed_cb = 5;
stream.first_missed_cb_ts.tv_sec = 100;
stream.first_missed_cb_ts.tv_nsec = 0;
+ stream.longest_fetch_interval.tv_sec = 1;
+ stream.longest_fetch_interval.tv_nsec = 0;
+ stream.sleep_interval_ts.tv_sec = 0;
+ stream.sleep_interval_ts.tv_nsec = 5000000;
stream.direction = CRAS_STREAM_INPUT;
stream.client_type = CRAS_CLIENT_TYPE_TEST;
+ stream.stream_type = CRAS_STREAM_TYPE_DEFAULT;
cras_server_metrics_stream_destroy(&stream);
subtract_timespecs(&clock_gettime_retspec, &stream.start_ts, &diff_ts);
- EXPECT_EQ(sent_msgs.size(), 3);
+ EXPECT_EQ(sent_msgs.size(), 4);
// Log missed cb frequency.
EXPECT_EQ(sent_msgs[0].header.type, CRAS_MAIN_METRICS);
@@ -315,9 +306,23 @@ TEST(ServerMetricsTestSuite, SetMetricsStreamDestroy) {
EXPECT_EQ(sent_msgs[2].header.length,
sizeof(struct cras_server_metrics_message));
EXPECT_EQ(sent_msgs[2].metrics_type, STREAM_RUNTIME);
- EXPECT_EQ(sent_msgs[2].data.stream_data.type, CRAS_CLIENT_TYPE_TEST);
+ EXPECT_EQ(sent_msgs[2].data.stream_data.client_type, CRAS_CLIENT_TYPE_TEST);
+ EXPECT_EQ(sent_msgs[2].data.stream_data.stream_type,
+ CRAS_STREAM_TYPE_DEFAULT);
EXPECT_EQ(sent_msgs[2].data.stream_data.direction, CRAS_STREAM_INPUT);
EXPECT_EQ(sent_msgs[2].data.stream_data.runtime.tv_sec, 1000);
+
+ // Log longest fetch delay.
+ EXPECT_EQ(sent_msgs[3].header.type, CRAS_MAIN_METRICS);
+ EXPECT_EQ(sent_msgs[3].header.length,
+ sizeof(struct cras_server_metrics_message));
+ EXPECT_EQ(sent_msgs[3].metrics_type, LONGEST_FETCH_DELAY);
+ EXPECT_EQ(sent_msgs[3].data.stream_data.client_type, CRAS_CLIENT_TYPE_TEST);
+ EXPECT_EQ(sent_msgs[3].data.stream_data.stream_type,
+ CRAS_STREAM_TYPE_DEFAULT);
+ EXPECT_EQ(sent_msgs[3].data.stream_data.direction, CRAS_STREAM_INPUT);
+ EXPECT_EQ(sent_msgs[3].data.stream_data.runtime.tv_sec, 0);
+ EXPECT_EQ(sent_msgs[3].data.stream_data.runtime.tv_nsec, 995000000);
}
TEST(ServerMetricsTestSuite, SetMetricsBusyloop) {
diff --git a/cras/src/tests/stream_list_unittest.cc b/cras/src/tests/stream_list_unittest.cc
index 40be35d0..500774f1 100644
--- a/cras/src/tests/stream_list_unittest.cc
+++ b/cras/src/tests/stream_list_unittest.cc
@@ -37,6 +37,10 @@ static int create_rstream_cb(struct cras_rstream_config* stream_config,
(*stream)->direction = stream_config->direction;
if (stream_config->format)
(*stream)->format = *(stream_config->format);
+ (*stream)->cb_threshold = stream_config->cb_threshold;
+ (*stream)->client_type = stream_config->client_type;
+ (*stream)->stream_type = stream_config->stream_type;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &(*stream)->start_ts);
return 0;
}
@@ -129,6 +133,68 @@ TEST(StreamList, AddInDescendingOrderByChannels) {
stream_list_destroy(l);
}
+TEST(StreamList, DetectRtcStreamPair) {
+ struct stream_list* l;
+ struct cras_rstream *s1, *s2, *s3, *s4;
+ struct cras_rstream_config s1_config, s2_config, s3_config, s4_config;
+
+ s1_config.stream_id = 0x5001;
+ s1_config.direction = CRAS_STREAM_OUTPUT;
+ s1_config.cb_threshold = 480;
+ s1_config.client_type = CRAS_CLIENT_TYPE_CHROME;
+ s1_config.stream_type = CRAS_STREAM_TYPE_DEFAULT;
+ s1_config.format = NULL;
+
+ s2_config.stream_id = 0x5002;
+ s2_config.direction = CRAS_STREAM_INPUT;
+ s2_config.cb_threshold = 480;
+ s2_config.client_type = CRAS_CLIENT_TYPE_CHROME;
+ s2_config.stream_type = CRAS_STREAM_TYPE_DEFAULT;
+ s2_config.format = NULL;
+
+ // s3 is not a RTC stream because the cb threshold is not 480.
+ s3_config.stream_id = 0x5003;
+ s3_config.direction = CRAS_STREAM_INPUT;
+ s3_config.cb_threshold = 500;
+ s3_config.client_type = CRAS_CLIENT_TYPE_CHROME;
+ s3_config.stream_type = CRAS_STREAM_TYPE_DEFAULT;
+ s3_config.format = NULL;
+
+ // s4 is not a RTC stream because it is not from the same client with s1.
+ s4_config.stream_id = 0x5004;
+ s4_config.direction = CRAS_STREAM_INPUT;
+ s4_config.cb_threshold = 480;
+ s4_config.client_type = CRAS_CLIENT_TYPE_LACROS;
+ s4_config.stream_type = CRAS_STREAM_TYPE_DEFAULT;
+ s4_config.format = NULL;
+
+ reset_test_data();
+ l = stream_list_create(added_cb, removed_cb, create_rstream_cb,
+ destroy_rstream_cb, NULL);
+ stream_list_add(l, &s1_config, &s1);
+ EXPECT_EQ(1, add_called);
+ EXPECT_EQ(1, create_called);
+ EXPECT_EQ(&s1_config, create_config);
+
+ stream_list_add(l, &s2_config, &s2);
+ detect_rtc_stream_pair(l, s2);
+ stream_list_add(l, &s3_config, &s3);
+ detect_rtc_stream_pair(l, s3);
+ stream_list_add(l, &s4_config, &s4);
+ detect_rtc_stream_pair(l, s4);
+
+ EXPECT_EQ(CRAS_STREAM_TYPE_VOICE_COMMUNICATION, s1->stream_type);
+ EXPECT_EQ(CRAS_STREAM_TYPE_VOICE_COMMUNICATION, s2->stream_type);
+ EXPECT_EQ(CRAS_STREAM_TYPE_DEFAULT, s3->stream_type);
+ EXPECT_EQ(CRAS_STREAM_TYPE_DEFAULT, s4->stream_type);
+
+ EXPECT_EQ(0, stream_list_rm(l, 0x5001));
+ EXPECT_EQ(0, stream_list_rm(l, 0x5002));
+ EXPECT_EQ(0, stream_list_rm(l, 0x5003));
+ EXPECT_EQ(0, stream_list_rm(l, 0x5004));
+ stream_list_destroy(l);
+}
+
extern "C" {
struct cras_timer* cras_tm_create_timer(struct cras_tm* tm,
diff --git a/cras/src/tests/system_state_unittest.cc b/cras/src/tests/system_state_unittest.cc
index 0450df38..45224bc9 100644
--- a/cras/src/tests/system_state_unittest.cc
+++ b/cras/src/tests/system_state_unittest.cc
@@ -39,7 +39,9 @@ static size_t cras_observer_notify_capture_mute_called;
static size_t cras_observer_notify_suspend_changed_called;
static size_t cras_observer_notify_num_active_streams_called;
static size_t cras_observer_notify_input_streams_with_permission_called;
+static size_t cras_iodev_list_reset_for_noise_cancellation_called;
static struct cras_board_config fake_board_config;
+static size_t cras_alert_process_all_pending_alerts_called;
static void ResetStubData() {
cras_alsa_card_create_called = 0;
@@ -60,6 +62,8 @@ static void ResetStubData() {
cras_observer_notify_suspend_changed_called = 0;
cras_observer_notify_num_active_streams_called = 0;
cras_observer_notify_input_streams_with_permission_called = 0;
+ cras_alert_process_all_pending_alerts_called = 0;
+ cras_iodev_list_reset_for_noise_cancellation_called = 0;
memset(&fake_board_config, 0, sizeof(fake_board_config));
}
@@ -275,6 +279,7 @@ TEST(SystemStateSuite, Suspend) {
cras_system_set_suspended(1);
EXPECT_EQ(1, cras_observer_notify_suspend_changed_called);
+ EXPECT_EQ(1, cras_alert_process_all_pending_alerts_called);
EXPECT_EQ(1, cras_system_get_suspended());
cras_system_set_suspended(0);
@@ -431,6 +436,33 @@ TEST(SystemStateSuite, IgnoreUCMSuffix) {
cras_system_state_deinit();
}
+TEST(SystemStateSuite, SetNoiseCancellationEnabled) {
+ ResetStubData();
+ do_sys_init();
+
+ EXPECT_EQ(0, cras_system_get_noise_cancellation_enabled());
+
+ cras_system_set_noise_cancellation_enabled(0);
+ EXPECT_EQ(0, cras_system_get_noise_cancellation_enabled());
+ EXPECT_EQ(0, cras_iodev_list_reset_for_noise_cancellation_called);
+
+ cras_system_set_noise_cancellation_enabled(1);
+ EXPECT_EQ(1, cras_system_get_noise_cancellation_enabled());
+ EXPECT_EQ(1, cras_iodev_list_reset_for_noise_cancellation_called);
+
+ cras_system_set_noise_cancellation_enabled(1);
+ EXPECT_EQ(1, cras_system_get_noise_cancellation_enabled());
+ // cras_iodev_list_reset_for_noise_cancellation shouldn't be called if state
+ // is already enabled/disabled.
+ EXPECT_EQ(1, cras_iodev_list_reset_for_noise_cancellation_called);
+
+ cras_system_set_noise_cancellation_enabled(0);
+ EXPECT_EQ(0, cras_system_get_noise_cancellation_enabled());
+ EXPECT_EQ(2, cras_iodev_list_reset_for_noise_cancellation_called);
+
+ cras_system_state_deinit();
+}
+
extern "C" {
struct cras_alsa_card* cras_alsa_card_create(
@@ -527,6 +559,14 @@ void cras_board_config_get(const char* config_path,
*board_config = fake_board_config;
}
+void cras_alert_process_all_pending_alerts() {
+ cras_alert_process_all_pending_alerts_called++;
+}
+
+void cras_iodev_list_reset_for_noise_cancellation() {
+ cras_iodev_list_reset_for_noise_cancellation_called++;
+}
+
} // extern "C"
} // namespace
diff --git a/cras/src/tests/timing_unittest.cc b/cras/src/tests/timing_unittest.cc
index 8a2de65f..964f30c3 100644
--- a/cras/src/tests/timing_unittest.cc
+++ b/cras/src/tests/timing_unittest.cc
@@ -111,20 +111,21 @@ int clock_gettime(clockid_t clk_id, struct timespec* tp) {
// Add a new input stream, make sure the initial next_cb_ts is 0.
TEST_F(TimingSuite, NewInputStreamInit) {
- struct open_dev* dev_list_ = NULL;
+ struct open_dev* odev_list_ = NULL;
+ struct open_dev* idev_list_ = NULL;
cras_audio_format format;
fill_audio_format(&format, 48000);
DevicePtr dev =
create_device(CRAS_STREAM_INPUT, 1024, &format, CRAS_NODE_TYPE_MIC);
- DL_APPEND(dev_list_, dev->odev.get());
+ DL_APPEND(idev_list_, dev->odev.get());
struct cras_iodev* iodev = dev->odev->dev;
ShmPtr shm = create_shm(480);
RstreamPtr rstream =
create_rstream(1, CRAS_STREAM_INPUT, 480, &format, shm.get());
- dev_io_append_stream(&dev_list_, rstream.get(), &iodev, 1);
+ dev_io_append_stream(&odev_list_, &idev_list_, rstream.get(), &iodev, 1);
EXPECT_EQ(0, rstream->next_cb_ts.tv_sec);
EXPECT_EQ(0, rstream->next_cb_ts.tv_nsec);
@@ -806,23 +807,68 @@ TEST_F(TimingSuite, HotwordStreamBulkDataIsNotPending) {
// When a new output stream is added, there are two rules to determine the
// initial next_cb_ts.
-// 1. If the device already has streams, the next_cb_ts will be the earliest
+// 1. If there is a matched input stream, use the next_cb_ts and
+// sleep_interval_ts from that input stream as the initial values.
+// 2. If the device already has streams, the next_cb_ts will be the earliest
// next callback time from these streams.
-// 2. If there are no other streams, the next_cb_ts will be set to the time
+// 3. If there are no other streams, the next_cb_ts will be set to the time
// when the valid frames in device is lower than cb_threshold. (If it is
// already lower than cb_threshold, set next_cb_ts to now.)
// Test rule 1.
+// There is a matched input stream. The next_cb_ts of the newly added output
+// stream will use the next_cb_ts from the input stream.
+TEST_F(TimingSuite, NewOutputStreamInitExistMatchedStream) {
+ struct open_dev* odev_list_ = NULL;
+ struct open_dev* idev_list_ = NULL;
+
+ cras_audio_format format;
+ fill_audio_format(&format, 48000);
+ DevicePtr out_dev = create_device(CRAS_STREAM_OUTPUT, 1024, &format,
+ CRAS_NODE_TYPE_HEADPHONE);
+ DL_APPEND(odev_list_, out_dev->odev.get());
+ struct cras_iodev* out_iodev = out_dev->odev->dev;
+
+ DevicePtr in_dev =
+ create_device(CRAS_STREAM_INPUT, 1024, &format, CRAS_NODE_TYPE_MIC);
+ DL_APPEND(idev_list_, in_dev->odev.get());
+
+ StreamPtr in_stream = create_stream(1, 1, CRAS_STREAM_INPUT, 480, &format);
+ add_stream_to_dev(in_dev->dev, in_stream);
+ in_stream->rstream->next_cb_ts.tv_sec = 54321;
+ in_stream->rstream->next_cb_ts.tv_nsec = 12345;
+ in_stream->rstream->sleep_interval_ts.tv_sec = 321;
+ in_stream->rstream->sleep_interval_ts.tv_nsec = 123;
+
+ ShmPtr shm = create_shm(480);
+ RstreamPtr rstream =
+ create_rstream(1, CRAS_STREAM_OUTPUT, 480, &format, shm.get());
+
+ dev_io_append_stream(&odev_list_, &idev_list_, rstream.get(), &out_iodev, 1);
+
+ EXPECT_EQ(in_stream->rstream->next_cb_ts.tv_sec, rstream->next_cb_ts.tv_sec);
+ EXPECT_EQ(in_stream->rstream->next_cb_ts.tv_nsec,
+ rstream->next_cb_ts.tv_nsec);
+ EXPECT_EQ(in_stream->rstream->sleep_interval_ts.tv_sec,
+ rstream->sleep_interval_ts.tv_sec);
+ EXPECT_EQ(in_stream->rstream->sleep_interval_ts.tv_nsec,
+ rstream->sleep_interval_ts.tv_nsec);
+
+ dev_stream_destroy(out_iodev->streams);
+}
+
+// Test rule 2.
// The device already has streams, the next_cb_ts will be the earliest
// next_cb_ts from these streams.
TEST_F(TimingSuite, NewOutputStreamInitStreamInDevice) {
- struct open_dev* dev_list_ = NULL;
+ struct open_dev* odev_list_ = NULL;
+ struct open_dev* idev_list_ = NULL;
cras_audio_format format;
fill_audio_format(&format, 48000);
DevicePtr dev = create_device(CRAS_STREAM_OUTPUT, 1024, &format,
CRAS_NODE_TYPE_HEADPHONE);
- DL_APPEND(dev_list_, dev->odev.get());
+ DL_APPEND(odev_list_, dev->odev.get());
struct cras_iodev* iodev = dev->odev->dev;
StreamPtr stream = create_stream(1, 1, CRAS_STREAM_OUTPUT, 480, &format);
@@ -834,7 +880,7 @@ TEST_F(TimingSuite, NewOutputStreamInitStreamInDevice) {
RstreamPtr rstream =
create_rstream(1, CRAS_STREAM_OUTPUT, 480, &format, shm.get());
- dev_io_append_stream(&dev_list_, rstream.get(), &iodev, 1);
+ dev_io_append_stream(&odev_list_, &idev_list_, rstream.get(), &iodev, 1);
EXPECT_EQ(stream->rstream->next_cb_ts.tv_sec, rstream->next_cb_ts.tv_sec);
EXPECT_EQ(stream->rstream->next_cb_ts.tv_nsec, rstream->next_cb_ts.tv_nsec);
@@ -842,17 +888,18 @@ TEST_F(TimingSuite, NewOutputStreamInitStreamInDevice) {
dev_stream_destroy(iodev->streams->next);
}
-// Test rule 2.
+// Test rule 3.
// The there are no streams and no frames in device buffer. The next_cb_ts
// will be set to now.
TEST_F(TimingSuite, NewOutputStreamInitNoStreamNoFramesInDevice) {
- struct open_dev* dev_list_ = NULL;
+ struct open_dev* odev_list_ = NULL;
+ struct open_dev* idev_list_ = NULL;
cras_audio_format format;
fill_audio_format(&format, 48000);
DevicePtr dev = create_device(CRAS_STREAM_OUTPUT, 1024, &format,
CRAS_NODE_TYPE_HEADPHONE);
- DL_APPEND(dev_list_, dev->odev.get());
+ DL_APPEND(odev_list_, dev->odev.get());
struct cras_iodev* iodev = dev->odev->dev;
struct timespec start;
@@ -862,7 +909,7 @@ TEST_F(TimingSuite, NewOutputStreamInitNoStreamNoFramesInDevice) {
RstreamPtr rstream =
create_rstream(1, CRAS_STREAM_OUTPUT, 480, &format, shm.get());
- dev_io_append_stream(&dev_list_, rstream.get(), &iodev, 1);
+ dev_io_append_stream(&odev_list_, &idev_list_, rstream.get(), &iodev, 1);
EXPECT_EQ(start.tv_sec, rstream->next_cb_ts.tv_sec);
EXPECT_EQ(start.tv_nsec, rstream->next_cb_ts.tv_nsec);
@@ -875,13 +922,14 @@ TEST_F(TimingSuite, NewOutputStreamInitNoStreamNoFramesInDevice) {
// next_cb_ts will be set to the time that valid frames in device is lower
// than cb_threshold.
TEST_F(TimingSuite, NewOutputStreamInitNoStreamSomeFramesInDevice) {
- struct open_dev* dev_list_ = NULL;
+ struct open_dev* odev_list_ = NULL;
+ struct open_dev* idev_list_ = NULL;
cras_audio_format format;
fill_audio_format(&format, 48000);
DevicePtr dev = create_device(CRAS_STREAM_OUTPUT, 1024, &format,
CRAS_NODE_TYPE_HEADPHONE);
- DL_APPEND(dev_list_, dev->odev.get());
+ DL_APPEND(odev_list_, dev->odev.get());
struct cras_iodev* iodev = dev->odev->dev;
struct timespec start;
@@ -893,7 +941,7 @@ TEST_F(TimingSuite, NewOutputStreamInitNoStreamSomeFramesInDevice) {
RstreamPtr rstream =
create_rstream(1, CRAS_STREAM_OUTPUT, 480, &format, shm.get());
- dev_io_append_stream(&dev_list_, rstream.get(), &iodev, 1);
+ dev_io_append_stream(&odev_list_, &idev_list_, rstream.get(), &iodev, 1);
// The next_cb_ts should be 10ms from now. At that time there are
// only 480 valid frames in the device.
diff --git a/cras/src/tools/cras_test_client/cras_test_client.c b/cras/src/tools/cras_test_client/cras_test_client.c
index 5a7b3e06..7a851852 100644
--- a/cras/src/tools/cras_test_client/cras_test_client.c
+++ b/cras/src/tools/cras_test_client/cras_test_client.c
@@ -1017,10 +1017,13 @@ static void show_btlog_tag(const struct cras_bt_event_log *log,
printf("%-30s dir %u codec id %u\n", "CODEC_SELECTION", data1,
data2);
break;
- case BT_DEV_CONNECTED_CHANGE:
- printf("%-30s supported profiles 0x%.2x now %s\n",
- "DEV_CONNECTED_CHANGE", data1,
- data2 ? "connected" : "disconnected");
+ case BT_DEV_CONNECTED:
+ printf("%-30s supported profiles 0x%.2x stable_id 0x%08x\n",
+ "DEV_CONNECTED", data1, data2);
+ break;
+ case BT_DEV_DISCONNECTED:
+ printf("%-30s supported profiles 0x%.2x stable_id 0x%08x\n",
+ "DEV_DISCONNECTED", data1, data2);
break;
case BT_DEV_CONN_WATCH_CB:
printf("%-30s %u retries left, supported profiles 0x%.2x\n",