Merge remote-tracking branch 'aosp/upstream-master' into HEAD am: 4e8152bdc5
Original change: https://android-review.googlesource.com/c/platform/external/adhd/+/1469642
Change-Id: Ifcf05716bae476431b7e7e451bee424399e7beda
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000..f8818dc
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,65 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+name: "CodeQL"
+
+on:
+ push:
+ branches: [master]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [master]
+ schedule:
+ - cron: '0 11 * * 1'
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ # Override automatic language detection by changing the below list
+ # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
+ language: ['cpp']
+ # Learn more...
+ # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+ with:
+ # We must fetch at least the immediate parents so that if this is
+ # a pull request then we can checkout the head.
+ fetch-depth: 2
+
+ # If this run was triggered by a pull request event, then checkout
+ # the head of the pull request instead of the merge commit.
+ - run: git checkout HEAD^2
+ if: ${{ github.event_name == 'pull_request' }}
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v1
+ with:
+ languages: ${{ matrix.language }}
+
+ # Command-line programs to run using the OS shell.
+ # https://git.io/JvXDl
+
+ - name: Install cras deps
+ working-directory: ./cras
+ run: sudo ./install_deps.sh
+
+ - name: Build
+ working-directory: ./cras
+ run: |
+ ./git_prepare.sh
+ ./configure
+ make
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v1
diff --git a/PRESUBMIT.cfg b/PRESUBMIT.cfg
index c0e6ae9..e85e5dc 100644
--- a/PRESUBMIT.cfg
+++ b/PRESUBMIT.cfg
@@ -1,6 +1,7 @@
[Hook Overrides]
tab_check: false
clang_format_check: true
+cargo_clippy_check: true
# On by default, but required for options below.
cros_license_check: true
@@ -10,3 +11,9 @@
cros_license_check: --exclude_regex=HiFi\.conf$
clang_format_check:
cras/
+cargo_clippy_check:
+ --project=audio_streams
+ --project=cras/client/cras-sys
+ --project=cras/client/cras_tests
+ --project=cras/client/libcras
+ --project=cras/src/server/rust
diff --git a/audio_streams/src/audio_streams.rs b/audio_streams/src/audio_streams.rs
index b83cc8c..3cb1173 100644
--- a/audio_streams/src/audio_streams.rs
+++ b/audio_streams/src/audio_streams.rs
@@ -134,22 +134,24 @@
pub trait StreamSource: Send {
/// Returns a stream control and buffer generator object. These are separate as the buffer
/// generator might want to be passed to the audio stream.
+ #[allow(clippy::type_complexity)]
fn new_playback_stream(
&mut self,
num_channels: usize,
format: SampleFormat,
- frame_rate: usize,
+ frame_rate: u32,
buffer_size: usize,
) -> Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError>;
/// Returns a stream control and buffer generator object. These are separate as the buffer
/// generator might want to be passed to the audio stream.
/// Default implementation returns `DummyStreamControl` and `DummyCaptureStream`.
+ #[allow(clippy::type_complexity)]
fn new_capture_stream(
&mut self,
num_channels: usize,
format: SampleFormat,
- frame_rate: usize,
+ frame_rate: u32,
buffer_size: usize,
) -> Result<
(
@@ -318,7 +320,7 @@
pub fn new(
num_channels: usize,
format: SampleFormat,
- frame_rate: usize,
+ frame_rate: u32,
buffer_size: usize,
) -> Self {
let frame_size = format.sample_bytes() * num_channels;
@@ -378,11 +380,12 @@
}
impl StreamSource for DummyStreamSource {
+ #[allow(clippy::type_complexity)]
fn new_playback_stream(
&mut self,
num_channels: usize,
format: SampleFormat,
- frame_rate: usize,
+ frame_rate: u32,
buffer_size: usize,
) -> Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError> {
Ok((
@@ -426,7 +429,7 @@
const FRAME_SIZE: usize = 4;
let mut buf = [0u8; 480 * FRAME_SIZE];
let mut pb_buf = PlaybackBuffer::new(FRAME_SIZE, &mut buf, &mut test_drop).unwrap();
- pb_buf.write(&[0xa5u8; 480 * FRAME_SIZE]).unwrap();
+ pb_buf.write_all(&[0xa5u8; 480 * FRAME_SIZE]).unwrap();
}
assert_eq!(test_drop.frame_count, 480);
}
diff --git a/audio_streams/src/capture.rs b/audio_streams/src/capture.rs
index a1313b1..3f2de3f 100644
--- a/audio_streams/src/capture.rs
+++ b/audio_streams/src/capture.rs
@@ -136,7 +136,7 @@
pub fn new(
num_channels: usize,
format: SampleFormat,
- frame_rate: usize,
+ frame_rate: u32,
buffer_size: usize,
) -> Self {
let frame_size = format.sample_bytes() * num_channels;
@@ -233,8 +233,8 @@
let mut stream_buffer = stream.next_capture_buffer().unwrap();
let mut cp_buf = [0xa5u8; 480 * 2 * 2];
assert_eq!(stream_buffer.read(&mut cp_buf).unwrap(), 480 * 2 * 2);
- for i in 0..cp_buf.len() {
- assert_eq!(cp_buf[i], 0, "Read samples should all be zeros.");
+ for buf in cp_buf.iter() {
+ assert_eq!(*buf, 0, "Read samples should all be zeros.");
}
}
// The second call should block until the first buffer is consumed.
diff --git a/audio_streams/src/shm_streams.rs b/audio_streams/src/shm_streams.rs
index 8597896..13475cd 100644
--- a/audio_streams/src/shm_streams.rs
+++ b/audio_streams/src/shm_streams.rs
@@ -129,6 +129,9 @@
/// Get the number of channels of audio data for this stream.
fn num_channels(&self) -> usize;
+ /// Get the frame rate of audio data for this stream.
+ fn frame_rate(&self) -> u32;
+
/// Waits until the next server message indicating action is required.
///
/// For playback streams, this will be `AUDIO_MESSAGE_REQUEST_DATA`, meaning
@@ -189,12 +192,13 @@
/// # Errors
///
/// * If sending the connect stream message to the server fails.
+ #[allow(clippy::too_many_arguments)]
fn new_stream(
&mut self,
direction: StreamDirection,
num_channels: usize,
format: SampleFormat,
- frame_rate: usize,
+ frame_rate: u32,
buffer_size: usize,
effects: &[StreamEffect],
client_shm: &SharedMemory,
@@ -214,6 +218,7 @@
/// Class that implements ShmStream trait but does nothing with the samples
pub struct NullShmStream {
num_channels: usize,
+ frame_rate: u32,
buffer_size: usize,
frame_size: usize,
interval: Duration,
@@ -223,16 +228,17 @@
impl NullShmStream {
/// Attempt to create a new NullShmStream with the given number of channels,
- /// format, and buffer_size.
+ /// format, frame_rate, and buffer_size.
pub fn new(
buffer_size: usize,
num_channels: usize,
format: SampleFormat,
- frame_rate: usize,
+ frame_rate: u32,
) -> Self {
let interval = Duration::from_millis(buffer_size as u64 * 1000 / frame_rate as u64);
Self {
num_channels,
+ frame_rate,
buffer_size,
frame_size: format.sample_bytes() * num_channels,
interval,
@@ -261,6 +267,10 @@
self.num_channels
}
+ fn frame_rate(&self) -> u32 {
+ self.frame_rate
+ }
+
fn wait_for_next_action_with_timeout<'a>(
&'a mut self,
timeout: Duration,
@@ -295,7 +305,7 @@
_direction: StreamDirection,
num_channels: usize,
format: SampleFormat,
- frame_rate: usize,
+ frame_rate: u32,
buffer_size: usize,
_effects: &[StreamEffect],
_client_shm: &SharedMemory,
@@ -309,6 +319,7 @@
#[derive(Clone)]
pub struct MockShmStream {
num_channels: usize,
+ frame_rate: u32,
request_size: usize,
frame_size: usize,
request_notifier: Arc<(Mutex<bool>, Condvar)>,
@@ -316,10 +327,16 @@
impl MockShmStream {
/// Attempt to create a new MockShmStream with the given number of
- /// channels, format, and buffer_size.
- pub fn new(num_channels: usize, format: SampleFormat, buffer_size: usize) -> Self {
+ /// channels, frame_rate, format, and buffer_size.
+ pub fn new(
+ num_channels: usize,
+ frame_rate: u32,
+ format: SampleFormat,
+ buffer_size: usize,
+ ) -> Self {
Self {
num_channels,
+ frame_rate,
request_size: buffer_size,
frame_size: format.sample_bytes() * num_channels,
request_notifier: Arc::new((Mutex::new(false), Condvar::new())),
@@ -346,7 +363,7 @@
}
}
- return true;
+ true
}
fn notify_request(&mut self) {
@@ -378,6 +395,10 @@
self.num_channels
}
+ fn frame_rate(&self) -> u32 {
+ self.frame_rate
+ }
+
fn wait_for_next_action_with_timeout<'a>(
&'a mut self,
timeout: Duration,
@@ -429,7 +450,7 @@
_direction: StreamDirection,
num_channels: usize,
format: SampleFormat,
- _frame_rate: usize,
+ frame_rate: u32,
buffer_size: usize,
_effects: &[StreamEffect],
_client_shm: &SharedMemory,
@@ -438,7 +459,7 @@
let &(ref last_stream, ref cvar) = &*self.last_stream;
let mut stream = last_stream.lock();
- let new_stream = MockShmStream::new(num_channels, format, buffer_size);
+ let new_stream = MockShmStream::new(num_channels, frame_rate, format, buffer_size);
*stream = Some(new_stream.clone());
cvar.notify_one();
Ok(Box::new(new_stream))
@@ -476,7 +497,7 @@
let request = stream
.wait_for_next_action_with_timeout(Duration::from_secs(5))
.expect("Failed to wait for next action");
- let result = match request {
+ match request {
Some(r) => {
let requested = r.requested_frames();
r.set_buffer_offset_and_frames(872, requested)
@@ -484,8 +505,7 @@
requested
}
None => 0,
- };
- result
+ }
});
let mut stream = stream_source.get_last_stream();
diff --git a/cras/README.dbus-api b/cras/README.dbus-api
index 26cfcad..243107a 100644
--- a/cras/README.dbus-api
+++ b/cras/README.dbus-api
@@ -33,7 +33,8 @@
void SetInputNodeGain(uint64 node_id, int32 gain)
Sets the capture gain of the node. gain is a 0-100
- integer which linearly maps to range [-40dB, 40dB].
+ integer which linearly maps [0, 50] to range [-40dB, 0dB]
+ and [50, 100] to [0dB, 20dB],
Default gain value is 50, which is 0dB.
void SetInputMute(boolean mute_on)
@@ -104,9 +105,6 @@
uint64 PluggedTime
The time that this device was plugged
in. This value is in microseconds.
- string MicPositions
- The string formed by floating numbers
- describing the position of mic array.
unit64 NodeVolume
The node volume indexed from 0 to 100.
unit64 NodeCaptureGain
diff --git a/cras/client/cras-sys/src/gen.rs b/cras/client/cras-sys/src/gen.rs
index 611f8b6..129a91a 100644
--- a/cras/client/cras-sys/src/gen.rs
+++ b/cras/client/cras-sys/src/gen.rs
@@ -31,7 +31,7 @@
pub const CRAS_SERV_MAX_MSG_SIZE: u32 = 256;
pub const CRAS_CLIENT_MAX_MSG_SIZE: u32 = 256;
pub const CRAS_MAX_HOTWORD_MODELS: u32 = 243;
-pub const CRAS_MAX_REMIX_CHANNELS: u32 = 32;
+pub const CRAS_MAX_REMIX_CHANNELS: u32 = 8;
pub const CRAS_MAX_TEST_DATA_LEN: u32 = 224;
pub const CRAS_AEC_DUMP_FILE_NAME_LEN: u32 = 128;
pub const CRAS_NUM_SHM_BUFFERS: u32 = 2;
@@ -50,12 +50,13 @@
pub idx: u32,
pub name: [::std::os::raw::c_char; 64usize],
pub stable_id: u32,
+ pub max_supported_channels: u32,
}
#[test]
fn bindgen_test_layout_cras_iodev_info() {
assert_eq!(
::std::mem::size_of::<cras_iodev_info>(),
- 72usize,
+ 76usize,
concat!("Size of: ", stringify!(cras_iodev_info))
);
assert_eq!(
@@ -93,6 +94,18 @@
stringify!(stable_id)
)
);
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<cras_iodev_info>())).max_supported_channels as *const _ as usize
+ },
+ 72usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(cras_iodev_info),
+ "::",
+ stringify!(max_supported_channels)
+ )
+ );
}
#[repr(C, packed)]
#[derive(Copy, Clone)]
@@ -108,7 +121,6 @@
pub left_right_swapped: i32,
pub type_enum: u32,
pub stable_id: u32,
- pub mic_positions: [::std::os::raw::c_char; 128usize],
pub type_: [::std::os::raw::c_char; 32usize],
pub name: [::std::os::raw::c_char; 64usize],
pub active_hotword_model: [::std::os::raw::c_char; 16usize],
@@ -160,7 +172,7 @@
fn bindgen_test_layout_cras_ionode_info() {
assert_eq!(
::std::mem::size_of::<cras_ionode_info>(),
- 296usize,
+ 168usize,
concat!("Size of: ", stringify!(cras_ionode_info))
);
assert_eq!(
@@ -281,18 +293,8 @@
)
);
assert_eq!(
- unsafe { &(*(::std::ptr::null::<cras_ionode_info>())).mic_positions as *const _ as usize },
- 56usize,
- concat!(
- "Offset of field: ",
- stringify!(cras_ionode_info),
- "::",
- stringify!(mic_positions)
- )
- );
- assert_eq!(
unsafe { &(*(::std::ptr::null::<cras_ionode_info>())).type_ as *const _ as usize },
- 184usize,
+ 56usize,
concat!(
"Offset of field: ",
stringify!(cras_ionode_info),
@@ -302,7 +304,7 @@
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<cras_ionode_info>())).name as *const _ as usize },
- 216usize,
+ 88usize,
concat!(
"Offset of field: ",
stringify!(cras_ionode_info),
@@ -314,7 +316,7 @@
unsafe {
&(*(::std::ptr::null::<cras_ionode_info>())).active_hotword_model as *const _ as usize
},
- 280usize,
+ 152usize,
concat!(
"Offset of field: ",
stringify!(cras_ionode_info),
@@ -719,6 +721,7 @@
CRAS_CLIENT_TYPE_ARC = 5,
CRAS_CLIENT_TYPE_CROSVM = 6,
CRAS_CLIENT_TYPE_SERVER_STREAM = 7,
+ CRAS_CLIENT_TYPE_LACROS = 8,
}
impl CRAS_STREAM_EFFECT {
pub const APM_ECHO_CANCELLATION: CRAS_STREAM_EFFECT = CRAS_STREAM_EFFECT(1);
@@ -841,13 +844,14 @@
BT_HFP_NEW_CONNECTION = 12,
BT_HFP_REQUEST_DISCONNECT = 13,
BT_HFP_SUPPORTED_FEATURES = 14,
- BT_HSP_NEW_CONNECTION = 15,
- BT_HSP_REQUEST_DISCONNECT = 16,
- BT_NEW_AUDIO_PROFILE_AFTER_CONNECT = 17,
- BT_RESET = 18,
- BT_SCO_CONNECT = 19,
- BT_TRANSPORT_ACQUIRE = 20,
- BT_TRANSPORT_RELEASE = 21,
+ BT_HFP_HF_INDICATOR = 15,
+ BT_HSP_NEW_CONNECTION = 16,
+ BT_HSP_REQUEST_DISCONNECT = 17,
+ BT_NEW_AUDIO_PROFILE_AFTER_CONNECT = 18,
+ BT_RESET = 19,
+ BT_SCO_CONNECT = 20,
+ BT_TRANSPORT_ACQUIRE = 21,
+ BT_TRANSPORT_RELEASE = 22,
}
#[repr(C, packed)]
#[derive(Debug, Copy, Clone)]
@@ -1580,6 +1584,145 @@
}
#[repr(C, packed)]
#[derive(Debug, Copy, Clone)]
+pub struct main_thread_event {
+ pub tag_sec: u32,
+ pub nsec: u32,
+ pub data1: u32,
+ pub data2: u32,
+}
+#[test]
+fn bindgen_test_layout_main_thread_event() {
+ assert_eq!(
+ ::std::mem::size_of::<main_thread_event>(),
+ 16usize,
+ concat!("Size of: ", stringify!(main_thread_event))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<main_thread_event>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(main_thread_event))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<main_thread_event>())).tag_sec as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(main_thread_event),
+ "::",
+ stringify!(tag_sec)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<main_thread_event>())).nsec as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(main_thread_event),
+ "::",
+ stringify!(nsec)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<main_thread_event>())).data1 as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(main_thread_event),
+ "::",
+ stringify!(data1)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<main_thread_event>())).data2 as *const _ as usize },
+ 12usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(main_thread_event),
+ "::",
+ stringify!(data2)
+ )
+ );
+}
+#[repr(C, packed)]
+#[derive(Copy, Clone)]
+pub struct main_thread_event_log {
+ pub write_pos: u32,
+ pub len: u32,
+ pub log: [main_thread_event; 1024usize],
+}
+#[test]
+fn bindgen_test_layout_main_thread_event_log() {
+ assert_eq!(
+ ::std::mem::size_of::<main_thread_event_log>(),
+ 16392usize,
+ concat!("Size of: ", stringify!(main_thread_event_log))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<main_thread_event_log>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(main_thread_event_log))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<main_thread_event_log>())).write_pos as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(main_thread_event_log),
+ "::",
+ stringify!(write_pos)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<main_thread_event_log>())).len as *const _ as usize },
+ 4usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(main_thread_event_log),
+ "::",
+ stringify!(len)
+ )
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<main_thread_event_log>())).log as *const _ as usize },
+ 8usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(main_thread_event_log),
+ "::",
+ stringify!(log)
+ )
+ );
+}
+#[repr(C, packed)]
+#[derive(Copy, Clone)]
+pub struct main_thread_debug_info {
+ pub main_log: main_thread_event_log,
+}
+#[test]
+fn bindgen_test_layout_main_thread_debug_info() {
+ assert_eq!(
+ ::std::mem::size_of::<main_thread_debug_info>(),
+ 16392usize,
+ concat!("Size of: ", stringify!(main_thread_debug_info))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<main_thread_debug_info>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(main_thread_debug_info))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<main_thread_debug_info>())).main_log as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(main_thread_debug_info),
+ "::",
+ stringify!(main_log)
+ )
+ );
+}
+#[repr(C, packed)]
+#[derive(Debug, Copy, Clone)]
pub struct cras_bt_event {
pub tag_sec: u32,
pub nsec: u32,
@@ -1720,12 +1863,15 @@
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum CRAS_AUDIO_THREAD_EVENT_TYPE {
- AUDIO_THREAD_EVENT_BUSYLOOP = 0,
- AUDIO_THREAD_EVENT_DEBUG = 1,
- AUDIO_THREAD_EVENT_SEVERE_UNDERRUN = 2,
- AUDIO_THREAD_EVENT_UNDERRUN = 3,
- AUDIO_THREAD_EVENT_DROP_SAMPLES = 4,
- AUDIO_THREAD_EVENT_TYPE_COUNT = 5,
+ AUDIO_THREAD_EVENT_A2DP_OVERRUN = 0,
+ AUDIO_THREAD_EVENT_A2DP_THROTTLE = 1,
+ AUDIO_THREAD_EVENT_BUSYLOOP = 2,
+ AUDIO_THREAD_EVENT_DEBUG = 3,
+ AUDIO_THREAD_EVENT_SEVERE_UNDERRUN = 4,
+ AUDIO_THREAD_EVENT_UNDERRUN = 5,
+ AUDIO_THREAD_EVENT_DROP_SAMPLES = 6,
+ AUDIO_THREAD_EVENT_DEV_OVERRUN = 7,
+ AUDIO_THREAD_EVENT_TYPE_COUNT = 8,
}
#[repr(C, packed)]
#[derive(Copy, Clone)]
@@ -1867,12 +2013,13 @@
pub snapshot_buffer: cras_audio_thread_snapshot_buffer,
pub bt_debug_info: cras_bt_debug_info,
pub bt_wbs_enabled: i32,
+ pub main_thread_debug_info: main_thread_debug_info,
}
#[test]
fn bindgen_test_layout_cras_server_state() {
assert_eq!(
::std::mem::size_of::<cras_server_state>(),
- 1398664usize,
+ 1410096usize,
concat!("Size of: ", stringify!(cras_server_state))
);
assert_eq!(
@@ -2044,7 +2191,7 @@
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<cras_server_state>())).input_devs as *const _ as usize },
- 1496usize,
+ 1576usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2056,7 +2203,7 @@
unsafe {
&(*(::std::ptr::null::<cras_server_state>())).num_output_nodes as *const _ as usize
},
- 2936usize,
+ 3096usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2068,7 +2215,7 @@
unsafe {
&(*(::std::ptr::null::<cras_server_state>())).num_input_nodes as *const _ as usize
},
- 2940usize,
+ 3100usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2078,7 +2225,7 @@
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<cras_server_state>())).output_nodes as *const _ as usize },
- 2944usize,
+ 3104usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2088,7 +2235,7 @@
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<cras_server_state>())).input_nodes as *const _ as usize },
- 8864usize,
+ 6464usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2100,7 +2247,7 @@
unsafe {
&(*(::std::ptr::null::<cras_server_state>())).num_attached_clients as *const _ as usize
},
- 14784usize,
+ 9824usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2110,7 +2257,7 @@
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<cras_server_state>())).client_info as *const _ as usize },
- 14788usize,
+ 9828usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2120,7 +2267,7 @@
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<cras_server_state>())).update_count as *const _ as usize },
- 15108usize,
+ 10148usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2132,7 +2279,7 @@
unsafe {
&(*(::std::ptr::null::<cras_server_state>())).num_active_streams as *const _ as usize
},
- 15112usize,
+ 10152usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2145,7 +2292,7 @@
&(*(::std::ptr::null::<cras_server_state>())).last_active_stream_time as *const _
as usize
},
- 15128usize,
+ 10168usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2157,7 +2304,7 @@
unsafe {
&(*(::std::ptr::null::<cras_server_state>())).audio_debug_info as *const _ as usize
},
- 15144usize,
+ 10184usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2170,7 +2317,7 @@
&(*(::std::ptr::null::<cras_server_state>())).default_output_buffer_size as *const _
as usize
},
- 139408usize,
+ 134448usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2182,7 +2329,7 @@
unsafe {
&(*(::std::ptr::null::<cras_server_state>())).non_empty_status as *const _ as usize
},
- 139412usize,
+ 134452usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2192,7 +2339,7 @@
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<cras_server_state>())).aec_supported as *const _ as usize },
- 139416usize,
+ 134456usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2202,7 +2349,7 @@
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<cras_server_state>())).aec_group_id as *const _ as usize },
- 139420usize,
+ 134460usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2214,7 +2361,7 @@
unsafe {
&(*(::std::ptr::null::<cras_server_state>())).snapshot_buffer as *const _ as usize
},
- 139424usize,
+ 134464usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2224,7 +2371,7 @@
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<cras_server_state>())).bt_debug_info as *const _ as usize },
- 1382268usize,
+ 1377308usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2236,7 +2383,7 @@
unsafe {
&(*(::std::ptr::null::<cras_server_state>())).bt_wbs_enabled as *const _ as usize
},
- 1398660usize,
+ 1393700usize,
concat!(
"Offset of field: ",
stringify!(cras_server_state),
@@ -2244,6 +2391,19 @@
stringify!(bt_wbs_enabled)
)
);
+ assert_eq!(
+ unsafe {
+ &(*(::std::ptr::null::<cras_server_state>())).main_thread_debug_info as *const _
+ as usize
+ },
+ 1393704usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(cras_server_state),
+ "::",
+ stringify!(main_thread_debug_info)
+ )
+ );
}
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;
@@ -2366,6 +2526,7 @@
CRAS_NODE_TYPE_FALLBACK_ABNORMAL = 13,
CRAS_NODE_TYPE_UNKNOWN = 14,
CRAS_NODE_TYPE_ECHO_REFERENCE = 15,
+ CRAS_NODE_TYPE_ALSA_LOOPBACK = 16,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -2410,6 +2571,7 @@
CRAS_SERVER_DUMP_BT = 28,
CRAS_SERVER_SET_BT_WBS_ENABLED = 29,
CRAS_SERVER_GET_ATLOG_FD = 30,
+ CRAS_SERVER_DUMP_MAIN = 31,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -3378,6 +3540,34 @@
}
#[repr(C, packed)]
#[derive(Debug, Copy, Clone)]
+pub struct cras_dump_main {
+ pub header: cras_server_message,
+}
+#[test]
+fn bindgen_test_layout_cras_dump_main() {
+ assert_eq!(
+ ::std::mem::size_of::<cras_dump_main>(),
+ 8usize,
+ concat!("Size of: ", stringify!(cras_dump_main))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<cras_dump_main>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(cras_dump_main))
+ );
+ assert_eq!(
+ unsafe { &(*(::std::ptr::null::<cras_dump_main>())).header as *const _ as usize },
+ 0usize,
+ concat!(
+ "Offset of field: ",
+ stringify!(cras_dump_main),
+ "::",
+ stringify!(header)
+ )
+ );
+}
+#[repr(C, packed)]
+#[derive(Debug, Copy, Clone)]
pub struct cras_dump_bt {
pub header: cras_server_message,
}
@@ -3544,17 +3734,17 @@
);
}
#[repr(C, packed)]
-#[derive(Debug, Copy, Clone)]
+#[derive(Copy, Clone)]
pub struct cras_config_global_remix {
pub header: cras_server_message,
pub num_channels: ::std::os::raw::c_uint,
- pub coefficient: [f32; 32usize],
+ pub coefficient: [f32; 64usize],
}
#[test]
fn bindgen_test_layout_cras_config_global_remix() {
assert_eq!(
::std::mem::size_of::<cras_config_global_remix>(),
- 140usize,
+ 268usize,
concat!("Size of: ", stringify!(cras_config_global_remix))
);
assert_eq!(
diff --git a/cras/client/cras-sys/src/lib.rs b/cras/client/cras-sys/src/lib.rs
index 44709ce..8128575 100644
--- a/cras/client/cras-sys/src/lib.rs
+++ b/cras/client/cras-sys/src/lib.rs
@@ -4,6 +4,7 @@
extern crate audio_streams;
extern crate data_model;
+use std::cmp::min;
use std::convert::{TryFrom, TryInto};
use std::error;
use std::fmt;
@@ -79,29 +80,86 @@
impl cras_audio_format_packed {
/// Initializes `cras_audio_format_packed` from input parameters.
+ /// Field `channel_layout` will be assigned with default channel layout defined in
+ /// `Self::default_channel_layout`.
///
/// # Arguments
/// * `format` - Format in used.
/// * `rate` - Rate in used.
/// * `num_channels` - Number of channels in used.
+ /// * `direction` - Stream direction enumeration.
///
/// # Returns
/// Structure `cras_audio_format_packed`
- pub fn new(format: _snd_pcm_format, rate: usize, num_channels: usize) -> Self {
- let mut audio_format = Self {
+ pub fn new(
+ format: _snd_pcm_format,
+ rate: u32,
+ num_channels: usize,
+ direction: CRAS_STREAM_DIRECTION,
+ ) -> Self {
+ Self {
format: format as i32,
- frame_rate: rate as u32,
+ frame_rate: rate,
num_channels: num_channels as u32,
- channel_layout: [-1; CRAS_CHANNEL::CRAS_CH_MAX as usize],
- };
- for i in 0..CRAS_CHANNEL::CRAS_CH_MAX as usize {
- if i < num_channels {
- audio_format.channel_layout[i] = i as i8;
- } else {
- break;
+ channel_layout: Self::default_channel_layout(num_channels, direction),
+ }
+ }
+
+ /// Generates default channel layout by given number of channels and stream direction.
+ /// ```
+ /// use cras_sys::gen::{
+ /// _snd_pcm_format,
+ /// cras_audio_format_packed,
+ /// CRAS_STREAM_DIRECTION::*
+ /// };
+ /// let test_one = | num_channels, direction, expected_results | {
+ /// let default_channel_fmt = cras_audio_format_packed::new(
+ /// _snd_pcm_format::SND_PCM_FORMAT_S16,
+ /// 48000,
+ /// num_channels,
+ /// direction
+ /// );
+ /// assert_eq!(default_channel_fmt.channel_layout, expected_results);
+ /// };
+ /// test_one(2, CRAS_STREAM_OUTPUT, [0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1]);
+ /// test_one(4, CRAS_STREAM_OUTPUT, [0, 1, 2, 3, -1, -1, -1, -1, -1, -1, -1]);
+ /// test_one(6, CRAS_STREAM_OUTPUT, [0, 1, 4, 5, 2, 3, -1, -1, -1, -1, -1]);
+ /// test_one(2, CRAS_STREAM_INPUT, [0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1]);
+ /// test_one(4, CRAS_STREAM_INPUT, [0, 1, 2, 3, -1, -1, -1, -1, -1, -1, -1]);
+ /// test_one(6, CRAS_STREAM_INPUT, [0, 1, 2, 3, 4, 5, -1, -1, -1, -1, -1]);
+ /// ```
+ fn default_channel_layout(
+ num_channels: usize,
+ direction: CRAS_STREAM_DIRECTION,
+ ) -> [i8; CRAS_CHANNEL::CRAS_CH_MAX as usize] {
+ use {CRAS_CHANNEL::*, CRAS_STREAM_DIRECTION::*};
+
+ let mut channel_layout = [-1; CRAS_CH_MAX as usize];
+ match (num_channels, direction) {
+ (6, CRAS_STREAM_OUTPUT) => {
+ [
+ CRAS_CH_FL,
+ CRAS_CH_FR,
+ CRAS_CH_FC,
+ CRAS_CH_LFE,
+ CRAS_CH_RL,
+ CRAS_CH_RR,
+ ]
+ .iter()
+ .enumerate()
+ .for_each(|(idx, &channel)| channel_layout[channel as usize] = idx as i8);
+ }
+ _ => {
+ for (i, channel) in channel_layout
+ .iter_mut()
+ .enumerate()
+ .take(min(num_channels, CRAS_CH_MAX as usize))
+ {
+ *channel = i as i8;
+ }
}
}
- audio_format
+ channel_layout
}
}
@@ -121,6 +179,7 @@
idx: 0,
name: [0; 64usize],
stable_id: 0,
+ max_supported_channels: 0,
}
}
}
@@ -168,7 +227,6 @@
left_right_swapped: 0,
type_enum: 0,
stable_id: 0,
- mic_positions: [0; 128usize],
type_: [0; 32usize],
name: [0; 64usize],
active_hotword_model: [0; 16usize],
@@ -362,6 +420,7 @@
5 => Ok(CRAS_CLIENT_TYPE_ARC),
6 => Ok(CRAS_CLIENT_TYPE_CROSVM),
7 => Ok(CRAS_CLIENT_TYPE_SERVER_STREAM),
+ 8 => Ok(CRAS_CLIENT_TYPE_LACROS),
_ => Err(Error::InvalidClientType(client_type)),
}
}
diff --git a/cras/client/cras_tests/src/arguments.rs b/cras/client/cras_tests/src/arguments.rs
index b051edf..59e9ec2 100644
--- a/cras/client/cras_tests/src/arguments.rs
+++ b/cras/client/cras_tests/src/arguments.rs
@@ -132,7 +132,14 @@
pub buffer_size: Option<usize>,
pub num_channels: Option<usize>,
pub format: Option<SampleFormat>,
- pub frame_rate: Option<usize>,
+ pub frame_rate: Option<u32>,
+}
+
+fn get_u32_param(matches: &Matches, option_name: &str) -> Result<Option<u32>> {
+ matches.opt_get::<u32>(option_name).map_err(|e| {
+ let argument = matches.opt_str(option_name).unwrap_or_default();
+ Error::InvalidArgument(option_name.to_string(), argument, e.to_string())
+ })
}
fn get_usize_param(matches: &Matches, option_name: &str) -> Result<Option<usize>> {
@@ -183,7 +190,7 @@
}
let loopback_type = if matches.opt_defined("loopback") {
- match matches.opt_str("loopback").as_ref().map(|s| s.as_str()) {
+ match matches.opt_str("loopback").as_deref() {
Some("pre_dsp") => Some(LoopbackType::PreDsp),
Some("post_dsp") => Some(LoopbackType::PostDsp),
Some(s) => {
@@ -210,7 +217,7 @@
let extension = file_name
.extension()
.map(|s| s.to_string_lossy().into_owned());
- let file_type = match extension.as_ref().map(String::as_str) {
+ let file_type = match extension.as_deref() {
Some("wav") | Some("wave") => FileType::Wav,
Some("raw") | None => FileType::Raw,
Some(extension) => return Err(Error::InvalidFiletype(extension.to_string())),
@@ -218,8 +225,8 @@
let buffer_size = get_usize_param(&matches, "buffer_size")?;
let num_channels = get_usize_param(&matches, "channels")?;
- let frame_rate = get_usize_param(&matches, "rate")?;
- let format = match matches.opt_str("format").as_ref().map(|s| s.as_str()) {
+ let frame_rate = get_u32_param(&matches, "rate")?;
+ let format = match matches.opt_str("format").as_deref() {
Some("U8") => Some(SampleFormat::U8),
Some("S16_LE") => Some(SampleFormat::S16LE),
Some("S24_LE") => Some(SampleFormat::S24LE),
diff --git a/cras/client/cras_tests/src/audio.rs b/cras/client/cras_tests/src/audio.rs
index 3ef90c2..261ad63 100644
--- a/cras/client/cras_tests/src/audio.rs
+++ b/cras/client/cras_tests/src/audio.rs
@@ -95,7 +95,7 @@
wav_reader: WavReader<BufReader<File>>,
format: SampleFormat,
num_channels: usize,
- frame_rate: usize,
+ frame_rate: u32,
}
impl WavSource {
@@ -122,7 +122,7 @@
eprintln!("Warning: number of channels changed to {}", num_channels);
}
- let frame_rate = spec.sample_rate as usize;
+ let frame_rate = spec.sample_rate;
if opts.frame_rate.is_some() && Some(frame_rate) != opts.frame_rate {
eprintln!("Warning: frame rate changed to {}", frame_rate);
}
@@ -143,7 +143,7 @@
self.num_channels
}
- fn frame_rate(&self) -> usize {
+ fn frame_rate(&self) -> u32 {
self.frame_rate
}
}
@@ -248,11 +248,11 @@
path: P,
num_channels: usize,
format: SampleFormat,
- frame_rate: usize,
+ frame_rate: u32,
) -> Result<Self> {
let spec = WavSpec {
channels: num_channels as u16,
- sample_rate: frame_rate as u32,
+ sample_rate: frame_rate,
bits_per_sample: (format.sample_bytes() * 8) as u16,
sample_format: hound::SampleFormat::Int,
};
@@ -278,11 +278,11 @@
match self.format {
SampleFormat::U8 => {
for sample in samples {
- self.wav_writer.write_sample(*sample as i8).or_else(|e| {
- Err(io::Error::new(
+ self.wav_writer.write_sample(*sample as i8).map_err(|e| {
+ io::Error::new(
io::ErrorKind::Other,
format!("Failed to write sample: {}", e),
- ))
+ )
})?;
}
}
@@ -301,11 +301,11 @@
// on drop.
// The flush method only writes data from the i16_writer to the underlying
// WavWriter, it does not actually guarantee a flush to disk.
- writer.flush().or_else(|e| {
- Err(io::Error::new(
+ writer.flush().map_err(|e| {
+ io::Error::new(
io::ErrorKind::Other,
format!("Failed to flush SampleWriter: {}", e),
- ))
+ )
})?;
}
SampleFormat::S24LE | SampleFormat::S32LE => {
@@ -321,14 +321,14 @@
// even though the wav encoder does.
// TODO(fletcherw): add S24_LE support to hound.
if self.format == SampleFormat::S24LE {
- sample = sample << 8;
+ sample <<= 8;
}
- self.wav_writer.write_sample(sample).or_else(|e| {
- Err(io::Error::new(
+ self.wav_writer.write_sample(sample).map_err(|e| {
+ io::Error::new(
io::ErrorKind::Other,
format!("Failed to write sample: {}", e),
- ))
+ )
})?;
}
}
@@ -338,11 +338,11 @@
}
fn flush(&mut self) -> io::Result<()> {
- self.wav_writer.flush().or_else(|e| {
- Err(io::Error::new(
+ self.wav_writer.flush().map_err(|e| {
+ io::Error::new(
io::ErrorKind::Other,
format!("Failed to flush WavWriter: {}", e),
- ))
+ )
})
}
}
@@ -385,7 +385,6 @@
let loopback_node = cras_client
.input_nodes()
- .into_iter()
.find(|node| node.node_type == node_type)
.ok_or(Error::NoLoopbackNode(node_type))?;
diff --git a/cras/client/libcras/src/cras_shm_stream.rs b/cras/client/libcras/src/cras_shm_stream.rs
index a04f20e..f72cc07 100644
--- a/cras/client/libcras/src/cras_shm_stream.rs
+++ b/cras/client/libcras/src/cras_shm_stream.rs
@@ -46,6 +46,7 @@
header: CrasAudioHeader<'a>,
frame_size: usize,
num_channels: usize,
+ frame_rate: u32,
// The index of the next buffer within SHM to set the buffer offset for.
next_buffer_idx: usize,
}
@@ -71,12 +72,14 @@
/// # Errors
///
/// * If `header_fd` could not be successfully mmapped.
+ #[allow(clippy::too_many_arguments)]
pub fn try_new(
stream_id: u32,
server_socket: CrasServerSocket,
audio_socket: AudioSocket,
direction: StreamDirection,
num_channels: usize,
+ frame_rate: u32,
format: SampleFormat,
header_fd: CrasAudioShmHeaderFd,
samples_len: usize,
@@ -90,6 +93,7 @@
header,
frame_size: format.sample_bytes() * num_channels,
num_channels,
+ frame_rate,
// We have either sent zero or two offsets to the server, so we will
// need to update index 0 next.
next_buffer_idx: 0,
@@ -115,6 +119,10 @@
self.num_channels
}
+ fn frame_rate(&self) -> u32 {
+ self.frame_rate
+ }
+
fn wait_for_next_action_with_timeout(
&mut self,
timeout: Duration,
@@ -129,7 +137,7 @@
.read_audio_message_with_timeout(Some(timeout))?
{
Some(AudioMessage::Success { id, frames }) if id == expected_id => {
- return Ok(Some(ServerRequest::new(frames as usize, self)));
+ Ok(Some(ServerRequest::new(frames as usize, self)))
}
None => Ok(None),
_ => Err(Box::new(Error::MessageTypeError)),
diff --git a/cras/client/libcras/src/cras_stream.rs b/cras/client/libcras/src/cras_stream.rs
index f46649f..5914bfd 100644
--- a/cras/client/libcras/src/cras_stream.rs
+++ b/cras/client/libcras/src/cras_stream.rs
@@ -119,7 +119,7 @@
server_socket: CrasServerSocket,
block_size: u32,
direction: CRAS_STREAM_DIRECTION,
- rate: usize,
+ rate: u32,
num_channels: usize,
format: snd_pcm_format_t,
/// A structure for stream to interact with server audio thread.
@@ -134,12 +134,13 @@
///
/// # Returns
/// `CrasStream` - CRAS client stream.
+ #[allow(clippy::too_many_arguments)]
pub fn try_new(
stream_id: u32,
server_socket: CrasServerSocket,
block_size: u32,
direction: CRAS_STREAM_DIRECTION,
- rate: usize,
+ rate: u32,
num_channels: usize,
format: snd_pcm_format_t,
audio_sock: AudioSocket,
diff --git a/cras/client/libcras/src/libcras.rs b/cras/client/libcras/src/libcras.rs
index 1246b7b..62e8fdd 100644
--- a/cras/client/libcras/src/libcras.rs
+++ b/cras/client/libcras/src/libcras.rs
@@ -29,7 +29,7 @@
//! use audio_streams::{SampleFormat, StreamSource};
//!
//! const BUFFER_SIZE: usize = 256;
-//! const FRAME_RATE: usize = 44100;
+//! const FRAME_RATE: u32 = 44100;
//! const NUM_CHANNELS: usize = 2;
//! const FORMAT: SampleFormat = SampleFormat::S16LE;
//!
@@ -82,7 +82,7 @@
//! use audio_streams::{SampleFormat, StreamSource};
//!
//! const BUFFER_SIZE: usize = 256;
-//! const FRAME_RATE: usize = 44100;
+//! const FRAME_RATE: u32 = 44100;
//! const NUM_CHANNELS: usize = 2;
//! const FORMAT: SampleFormat = SampleFormat::S16LE;
//!
@@ -396,14 +396,15 @@
device_index: Option<u32>,
block_size: u32,
direction: CRAS_STREAM_DIRECTION,
- rate: usize,
+ rate: u32,
channel_num: usize,
format: SampleFormat,
) -> Result<CrasStream<'b, T>> {
let stream_id = self.next_server_stream_id();
// Prepares server message
- let audio_format = cras_audio_format_packed::new(format.into(), rate, channel_num);
+ let audio_format =
+ cras_audio_format_packed::new(format.into(), rate, channel_num, direction);
let msg_header = cras_server_message {
length: mem::size_of::<cras_connect_message>() as u32,
id: CRAS_SERVER_MESSAGE_ID::CRAS_SERVER_CONNECT_STREAM,
@@ -463,12 +464,13 @@
/// * `format` - The format to use for stream audio samples.
/// * `frame_rate` - The sample rate of the stream.
/// * `buffer_size` - The transfer size granularity in frames.
+ #[allow(clippy::type_complexity)]
pub fn new_pinned_playback_stream(
&mut self,
device_index: u32,
num_channels: usize,
format: SampleFormat,
- frame_rate: usize,
+ frame_rate: u32,
buffer_size: usize,
) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError>
{
@@ -497,12 +499,13 @@
/// * `format` - The format to use for stream audio samples.
/// * `frame_rate` - The sample rate of the stream.
/// * `buffer_size` - The transfer size granularity in frames.
+ #[allow(clippy::type_complexity)]
pub fn new_pinned_capture_stream(
&mut self,
device_index: u32,
num_channels: usize,
format: SampleFormat,
- frame_rate: usize,
+ frame_rate: u32,
buffer_size: usize,
) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn CaptureBufferStream>), BoxError> {
Ok((
@@ -549,11 +552,12 @@
}
impl<'a> StreamSource for CrasClient<'a> {
+ #[allow(clippy::type_complexity)]
fn new_playback_stream(
&mut self,
num_channels: usize,
format: SampleFormat,
- frame_rate: usize,
+ frame_rate: u32,
buffer_size: usize,
) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError>
{
@@ -570,11 +574,12 @@
))
}
+ #[allow(clippy::type_complexity)]
fn new_capture_stream(
&mut self,
num_channels: usize,
format: SampleFormat,
- frame_rate: usize,
+ frame_rate: u32,
buffer_size: usize,
) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn CaptureBufferStream>), BoxError> {
if self.cras_capture {
@@ -613,7 +618,7 @@
direction: StreamDirection,
num_channels: usize,
format: SampleFormat,
- frame_rate: usize,
+ frame_rate: u32,
buffer_size: usize,
effects: &[StreamEffect],
client_shm: &SharedMemory,
@@ -632,7 +637,12 @@
// Prepares server message
let stream_id = self.next_server_stream_id();
- let audio_format = cras_audio_format_packed::new(format.into(), frame_rate, num_channels);
+ let audio_format = cras_audio_format_packed::new(
+ format.into(),
+ frame_rate,
+ num_channels,
+ direction.into(),
+ );
let msg_header = cras_server_message {
length: mem::size_of::<cras_connect_message>() as u32,
id: CRAS_SERVER_MESSAGE_ID::CRAS_SERVER_CONNECT_STREAM,
@@ -649,7 +659,7 @@
flags: 0,
format: audio_format,
dev_idx: CRAS_SPECIAL_DEVICE::NO_DEVICE as u32,
- effects: effects.into_iter().collect::<CrasStreamEffect>().into(),
+ effects: effects.iter().collect::<CrasStreamEffect>().into(),
client_type: self.client_type,
client_shm_size: client_shm.size(),
buffer_offsets,
@@ -673,6 +683,7 @@
audio_socket,
direction,
num_channels,
+ frame_rate,
format,
header_fd,
client_shm.size() as usize,
diff --git a/cras/configure.ac b/cras/configure.ac
index 57030a5..f39a14a 100644
--- a/cras/configure.ac
+++ b/cras/configure.ac
@@ -71,6 +71,15 @@
fi
PKG_CHECK_MODULES([SBC], [ sbc >= 1.0 ])
+AC_CHECK_HEADERS([iniparser/iniparser.h iniparser.h], [FOUND_INIPARSER=1;break])
+test [$FOUND_INIPARSER] || AC_MSG_ERROR([Missing iniparser, please install.])
+AC_SEARCH_LIBS([LADSPA], [ladspa-sdk], [], [
+ AC_CHECK_HEADERS([ladspa.h], [], [
+ AC_MSG_ERROR([Missing ladspa-sdk, please install.])
+ ])
+])
+PKG_CHECK_MODULES([UDEV], [ libudev >= 1.0 ])
+PKG_CHECK_MODULES([GTEST], [ gtest >= 1.0 ])
AC_CHECK_LIB(asound, snd_pcm_ioplug_create,,
AC_ERROR([*** libasound has no external plugin SDK]), -ldl)
@@ -80,7 +89,7 @@
AC_ARG_ENABLE([metrics], AS_HELP_STRING([--enable-metrics], [Enable metrics uses]), have_metrics=$enableval, have_metrics=no)
if test "$have_metrics" = "yes"; then
AC_DEFINE(HAVE_LIB_METRICS, 1, [Define to use libmetrics])
- METRICS_LIBS=-lmetrics-${BASE_VER}
+ METRICS_LIBS=-lmetrics
else
METRICS_LIBS=
fi
diff --git a/cras/install_deps.sh b/cras/install_deps.sh
new file mode 100755
index 0000000..6eac01a
--- /dev/null
+++ b/cras/install_deps.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+apt-get install -y \
+ automake \
+ build-essential \
+ cmake \
+ g++ \
+ gdb \
+ git \
+ ladspa-sdk \
+ libasound-dev \
+ libdbus-1-dev \
+ libncurses5-dev \
+ libsbc-dev \
+ libsndfile-dev \
+ libspeexdsp-dev \
+ libtool \
+ libudev-dev \
+ wget \
+ zip
+cd /tmp
+git clone https://github.com/ndevilla/iniparser.git
+cd iniparser
+make
+cp libiniparser.* /usr/local/lib
+cp src/dictionary.h src/iniparser.h /usr/local/include
+chmod 644 /usr/local/include/dictionary.h /usr/local/include/iniparser.h
+chmod 644 /usr/local/lib/libiniparser.a
+chmod 755 /usr/local/lib/libiniparser.so.*
+
+cd /tmp
+git clone https://github.com/google/googletest.git -b v1.8.x
+cd googletest
+mkdir build
+cd build
+cmake .. -DBUILD_SHARED_LIBS=ON \
+ -DINSTALL_GTEST=ON \
+ -DCMAKE_INSTALL_PREFIX:PATH=/usr
+make
+make install
+
+# Need to build and install alsa so there is a static lib.
+mkdir -p /tmp/alsa-build &&
+ cd /tmp/alsa-build && \
+ wget ftp://ftp.alsa-project.org/pub/lib/alsa-lib-1.1.4.1.tar.bz2 && \
+ bzip2 -f -d alsa-lib-* && \
+ tar xf alsa-lib-* && \
+ cd alsa-lib-* && \
+ ./configure --enable-static --disable-shared && \
+ make clean && \
+ make -j$(nproc) all && \
+ make install
diff --git a/cras/src/Makefile.am b/cras/src/Makefile.am
index 2f98075..874389c 100644
--- a/cras/src/Makefile.am
+++ b/cras/src/Makefile.am
@@ -163,12 +163,14 @@
server/test_iodev.c \
server/softvol_curve.c
+SERVER_RUST_SRCDIR = $(top_srcdir)/src/server/rust
+
libcrasserver_la_SOURCES = \
$(cras_server_SOURCES)
libcrasserver_la_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/dsp -I$(top_srcdir)/src/server \
-I$(top_srcdir)/src/server/config -I$(top_srcdir)/src/plc \
- -I$(top_srcdir)/src/server/rust/src/headers \
+ -I$(SERVER_RUST_SRCDIR)/src/headers \
$(DBUS_CFLAGS) $(SBC_CFLAGS) $(SELINUX_CFLAGS)
libcrasserver_la_LIBADD = \
$(CRAS_RUST) \
@@ -306,14 +308,22 @@
if !WITH_SYSTEM_RUST
RUST_FILES = \
- server/rust/Cargo.toml \
- server/rust/src/rate_estimator_bindings.rs \
- server/rust/src/rate_estimator.rs
+ $(SERVER_RUST_SRCDIR)/Cargo.toml \
+ $(SERVER_RUST_SRCDIR)/src/rate_estimator_bindings.rs \
+ $(SERVER_RUST_SRCDIR)/src/rate_estimator.rs
-CRAS_RUST = libcras_rust.a
+CRAS_RUST_TARGET_DIR = $(top_builddir)/src/server/rust/target
+CRAS_RUST = $(CRAS_RUST_TARGET_DIR)/release/libcras_rust.a
$(CRAS_RUST): $(RUST_FILES)
- cargo build --release --manifest-path server/rust/Cargo.toml
- cp server/rust/target/release/libcras_rust.a .
+ cargo build --release \
+ --manifest-path $(SERVER_RUST_SRCDIR)/Cargo.toml \
+ --target-dir $(CRAS_RUST_TARGET_DIR)
+
+clean-local:
+ cargo clean --release \
+ --manifest-path $(SERVER_RUST_SRCDIR)/Cargo.toml \
+ --target-dir $(CRAS_RUST_TARGET_DIR)
+
else
CRAS_RUST = -lcras_rust
endif
@@ -661,7 +671,7 @@
common/cras_shm.c
audio_thread_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
-I$(top_srcdir)/src/common -I$(top_srcdir)/src/server \
- -I$(top_srcdir)/src/server/rust/src/headers
+ -I$(SERVER_RUST_SRCDIR)/src/headers
audio_thread_unittest_LDADD = -lgtest -lpthread -lrt
audio_thread_monitor_unittest_SOURCES = tests/audio_thread_monitor_unittest.cc
@@ -733,7 +743,7 @@
-I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/server \
-I$(top_srcdir)/src/server/config \
- -I$(top_srcdir)/src/server/rust/src/headers \
+ -I$(SERVER_RUST_SRCDIR)/src/headers \
$(SELINUX_CFLAGS)
dev_io_unittest_LDADD = \
libcrasmix.la \
@@ -893,7 +903,7 @@
server/cras_iodev.c common/cras_shm.c
iodev_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/server \
- -I$(top_srcdir)/src/server/rust/src/headers
+ -I$(SERVER_RUST_SRCDIR)/src/headers
iodev_unittest_LDADD = -lgtest -lpthread -lrt
mix_unittest_SOURCES = tests/mix_unittest.cc server/cras_mix.c
@@ -932,7 +942,7 @@
rate_estimator_unittest_SOURCES = tests/rate_estimator_unittest.cc
rate_estimator_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/server \
- -I$(top_srcdir)/src/server/rust/src/headers
+ -I$(SERVER_RUST_SRCDIR)/src/headers
rate_estimator_unittest_LDADD = $(CRAS_RUST) -lgtest -ldl -lpthread
control_rclient_unittest_SOURCES = tests/control_rclient_unittest.cc \
@@ -1018,7 +1028,7 @@
-I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/server \
-I$(top_srcdir)/src/server/config \
- -I$(top_srcdir)/src/server/rust/src/headers \
+ -I$(SERVER_RUST_SRCDIR)/src/headers \
$(SELINUX_CFLAGS)
timing_unittest_LDADD = \
libcrasmix.la \
diff --git a/cras/src/common/bluetooth.h b/cras/src/common/bluetooth.h
index f9a9b5e..66beada 100644
--- a/cras/src/common/bluetooth.h
+++ b/cras/src/common/bluetooth.h
@@ -75,6 +75,10 @@
#define BT_VOICE_TRANSPARENT 0x0003
-#define BT_PKT_STATUS 16
+#define BT_SNDMTU 12
-#define BT_SCM_PKT_STATUS 0x03
\ No newline at end of file
+#define BT_RCVMTU 13
+
+#define BT_PKT_STATUS 16
+
+#define BT_SCM_PKT_STATUS 0x03
diff --git a/cras/src/common/cras_iodev_info.h b/cras/src/common/cras_iodev_info.h
index 3a6af91..85d20f9 100644
--- a/cras/src/common/cras_iodev_info.h
+++ b/cras/src/common/cras_iodev_info.h
@@ -39,7 +39,6 @@
* ui_gain_scaler - Adjustable gain scaler set by Chrome.
* left_right_swapped - Set true if left and right channels are swapped.
* stable_id - ID that does not change due to device plug/unplug or reboot.
- * mic_positions - Positions of the mic array.
* type - Type displayed to the user.
* name - Name displayed to the user.
* active_hotword_model - name of the currently selected hotword model.
@@ -59,7 +58,6 @@
int32_t left_right_swapped;
uint32_t type_enum;
uint32_t stable_id;
- char mic_positions[CRAS_NODE_MIC_POS_BUFFER_SIZE];
char type[CRAS_NODE_TYPE_BUFFER_SIZE];
char name[CRAS_NODE_NAME_BUFFER_SIZE];
char active_hotword_model[CRAS_NODE_HOTWORD_MODEL_BUFFER_SIZE];
diff --git a/cras/src/common/cras_messages.h b/cras/src/common/cras_messages.h
index 7021808..ee09d93 100644
--- a/cras/src/common/cras_messages.h
+++ b/cras/src/common/cras_messages.h
@@ -58,6 +58,7 @@
CRAS_SERVER_DUMP_BT,
CRAS_SERVER_SET_BT_WBS_ENABLED,
CRAS_SERVER_GET_ATLOG_FD,
+ CRAS_SERVER_DUMP_MAIN,
};
enum CRAS_CLIENT_MESSAGE_ID {
@@ -352,6 +353,17 @@
m->header.length = sizeof(*m);
}
+/* Dump events in CRAS main thread. */
+struct __attribute__((__packed__)) cras_dump_main {
+ struct cras_server_message header;
+};
+
+static inline void cras_fill_dump_main(struct cras_dump_main *m)
+{
+ m->header.id = CRAS_SERVER_DUMP_MAIN;
+ m->header.length = sizeof(*m);
+}
+
/* Dump bluetooth events and state changes. */
struct __attribute__((__packed__)) cras_dump_bt {
struct cras_server_message header;
diff --git a/cras/src/common/cras_shm.h b/cras/src/common/cras_shm.h
index 6e954e2..47786c3 100644
--- a/cras/src/common/cras_shm.h
+++ b/cras/src/common/cras_shm.h
@@ -564,11 +564,12 @@
uint32_t i;
shm->config.used_size = used_size;
- if (shm->header)
+ if (shm->header) {
shm->header->config.used_size = used_size;
- for (i = 0; i < CRAS_NUM_SHM_BUFFERS; i++)
- cras_shm_set_buffer_offset(shm, i, i * used_size);
+ for (i = 0; i < CRAS_NUM_SHM_BUFFERS; i++)
+ cras_shm_set_buffer_offset(shm, i, i * used_size);
+ }
}
/* Returns the used size of the shm region in bytes. */
diff --git a/cras/src/common/cras_types.h b/cras/src/common/cras_types.h
index dc0b188..c3482e8 100644
--- a/cras/src/common/cras_types.h
+++ b/cras/src/common/cras_types.h
@@ -164,6 +164,7 @@
CRAS_CLIENT_TYPE_ARC, /* ARC++ */
CRAS_CLIENT_TYPE_CROSVM, /* CROSVM */
CRAS_CLIENT_TYPE_SERVER_STREAM, /* Server stream */
+ CRAS_CLIENT_TYPE_LACROS, /* LaCrOS */
};
#define ENUM_STR(x) \
@@ -200,6 +201,7 @@
ENUM_STR(CRAS_CLIENT_TYPE_ARC)
ENUM_STR(CRAS_CLIENT_TYPE_CROSVM)
ENUM_STR(CRAS_CLIENT_TYPE_SERVER_STREAM)
+ ENUM_STR(CRAS_CLIENT_TYPE_LACROS)
default:
return "INVALID_CLIENT_TYPE";
}
@@ -252,6 +254,7 @@
#define MAX_DEBUG_STREAMS 8
#define AUDIO_THREAD_EVENT_LOG_SIZE (1024 * 6)
#define CRAS_BT_EVENT_LOG_SIZE 1024
+#define MAIN_THREAD_EVENT_LOG_SIZE 1024
/* There are 8 bits of space for events. */
enum AUDIO_THREAD_LOG_EVENTS {
@@ -305,6 +308,44 @@
AUDIO_THREAD_DEV_OVERRUN,
};
+/* Important events in main thread.
+ * MAIN_THREAD_DEV_CLOSE - When an iodev closes at stream removal.
+ * MAIN_THREAD_DEV_DISABLE - When an iodev is removed from active dev list.
+ * MAIN_THREAD_DEV_INIT - When an iodev opens when stream attachs.
+ * MAIN_THREAD_DEV_REOPEN - When an iodev reopens for format change.
+ * MAIN_THREAD_ADD_ACTIVE_NODE - When an iodev is set as an additional
+ * active device.
+ * MAIN_THREAD_SELECT_NODE - When UI selects an iodev as active.
+ * MAIN_THREAD_NODE_PLUGGED - When a jack of iodev is plugged/unplugged.
+ * MAIN_THREAD_ADD_TO_DEV_LIST - When iodev is added to list.
+ * MAIN_THREAD_INPUT_NODE_GAIN - When input node gain changes.
+ * MAIN_THREAD_OUTPUT_NODE_VOLUME - When output node volume changes.
+ * MAIN_THREAD_SET_OUTPUT_USER_MUTE - When output mute state is set.
+ * MAIN_THREAD_RESUME_DEVS - When system resumes and notifies CRAS.
+ * MAIN_THREAD_SUSPEND_DEVS - When system suspends and notifies CRAS.
+ * MAIN_THREAD_STREAM_ADDED - When an audio stream is added.
+ * MAIN_THREAD_STREAM_REMOVED - When an audio stream is removed.
+ */
+enum MAIN_THREAD_LOG_EVENTS {
+ /* iodev related */
+ MAIN_THREAD_DEV_CLOSE,
+ MAIN_THREAD_DEV_DISABLE,
+ MAIN_THREAD_DEV_INIT,
+ MAIN_THREAD_DEV_REOPEN,
+ MAIN_THREAD_ADD_ACTIVE_NODE,
+ MAIN_THREAD_SELECT_NODE,
+ MAIN_THREAD_NODE_PLUGGED,
+ MAIN_THREAD_ADD_TO_DEV_LIST,
+ MAIN_THREAD_INPUT_NODE_GAIN,
+ MAIN_THREAD_OUTPUT_NODE_VOLUME,
+ MAIN_THREAD_SET_OUTPUT_USER_MUTE,
+ MAIN_THREAD_RESUME_DEVS,
+ MAIN_THREAD_SUSPEND_DEVS,
+ /* stream related */
+ MAIN_THREAD_STREAM_ADDED,
+ MAIN_THREAD_STREAM_REMOVED,
+};
+
/* There are 8 bits of space for events. */
enum CRAS_BT_LOG_EVENTS {
BT_ADAPTER_ADDED,
@@ -323,6 +364,8 @@
BT_HFP_REQUEST_DISCONNECT,
BT_HFP_SUPPORTED_FEATURES,
BT_HFP_HF_INDICATOR,
+ BT_HFP_SET_SPEAKER_GAIN,
+ BT_HFP_UPDATE_SPEAKER_GAIN,
BT_HSP_NEW_CONNECTION,
BT_HSP_REQUEST_DISCONNECT,
BT_NEW_AUDIO_PROFILE_AFTER_CONNECT,
@@ -330,6 +373,8 @@
BT_SCO_CONNECT,
BT_TRANSPORT_ACQUIRE,
BT_TRANSPORT_RELEASE,
+ BT_TRANSPORT_SET_VOLUME,
+ BT_TRANSPORT_UPDATE_VOLUME,
};
struct __attribute__((__packed__)) audio_thread_event {
@@ -401,6 +446,24 @@
struct audio_thread_event_log log;
};
+struct __attribute__((__packed__)) main_thread_event {
+ uint32_t tag_sec;
+ uint32_t nsec;
+ uint32_t data1;
+ uint32_t data2;
+ uint32_t data3;
+};
+
+struct __attribute__((__packed__)) main_thread_event_log {
+ uint32_t write_pos;
+ uint32_t len;
+ struct main_thread_event log[MAIN_THREAD_EVENT_LOG_SIZE];
+};
+
+struct __attribute__((__packed__)) main_thread_debug_info {
+ struct main_thread_event_log main_log;
+};
+
struct __attribute__((__packed__)) cras_bt_event {
uint32_t tag_sec;
uint32_t nsec;
@@ -493,6 +556,7 @@
* snapshot_buffer - ring buffer for storing audio thread snapshots.
* bt_debug_info - ring buffer for storing bluetooth event logs.
* bt_wbs_enabled - Whether or not bluetooth wideband speech is enabled.
+ * main_thread_debug_info - ring buffer for storing main thread event logs.
*/
#define CRAS_SERVER_STATE_VERSION 2
struct __attribute__((packed, aligned(4))) cras_server_state {
@@ -529,6 +593,7 @@
struct cras_audio_thread_snapshot_buffer snapshot_buffer;
struct cras_bt_debug_info bt_debug_info;
int32_t bt_wbs_enabled;
+ struct main_thread_debug_info main_thread_debug_info;
};
/* Actions for card add/remove/change. */
diff --git a/cras/src/common/dumper.c b/cras/src/common/dumper.c
index 50789e8..5da16df 100644
--- a/cras/src/common/dumper.c
+++ b/cras/src/common/dumper.c
@@ -108,18 +108,23 @@
struct dumper *dumper = calloc(1, sizeof(struct dumper));
struct mem_data *data = calloc(1, sizeof(struct mem_data));
if (!dumper || !data)
- return NULL;
+ goto error;
data->size = 0;
data->capacity = 80;
data->buf = malloc(data->capacity);
- if (!data->buf) {
- free(data);
- return NULL;
- }
+ if (!data->buf)
+ goto error;
data->buf[0] = '\0';
dumper->data = data;
dumper->vprintf = &mem_vprintf;
return dumper;
+
+error:
+ if (dumper)
+ free(dumper);
+ if (data)
+ free(data);
+ return NULL;
}
void mem_dumper_free(struct dumper *dumper)
diff --git a/cras/src/common/utlist.h b/cras/src/common/utlist.h
index e3ee630..6c7f1e3 100644
--- a/cras/src/common/utlist.h
+++ b/cras/src/common/utlist.h
@@ -194,6 +194,7 @@
#define DL_DELETE(head, del) \
do { \
+ assert((head) != NULL); \
assert((del)->prev != NULL); \
if ((del)->prev == (del)) { \
(head) = NULL; \
diff --git a/cras/src/fuzz/Dockerfile b/cras/src/fuzz/Dockerfile
index f8e9e58..caffa99 100644
--- a/cras/src/fuzz/Dockerfile
+++ b/cras/src/fuzz/Dockerfile
@@ -7,52 +7,7 @@
FROM gcr.io/oss-fuzz-base/base-builder
LABEL maintainer="dgreid@chromium.org"
-RUN apt-get -y update && \
- apt-get install -y \
- automake \
- build-essential \
- cmake \
- g++ \
- gdb \
- git \
- ladspa-sdk \
- libasound-dev \
- libdbus-1-dev \
- libgtest-dev \
- libncurses5-dev \
- libsbc-dev \
- libsndfile-dev \
- libspeexdsp-dev \
- libtool \
- libudev-dev \
- wget \
- zip
-RUN apt-get clean
-RUN cd /tmp && git clone https://github.com/ndevilla/iniparser.git && \
- cd iniparser && \
- make && \
- cp libiniparser.* /usr/local/lib && \
- cp src/dictionary.h src/iniparser.h /usr/local/include && \
- chmod 644 /usr/local/include/dictionary.h /usr/local/include/iniparser.h && \
- chmod 644 /usr/local/lib/libiniparser.a && \
- chmod 755 /usr/local/lib/libiniparser.so.*
-RUN cd /usr/src/gtest && \
- cmake . && \
- make && \
- chmod 644 *.a && \
- cp *.a /usr/local/lib
-
-# Need to build and install alsa so there is a static lib.
-RUN mkdir -p /tmp/alsa-build && cd /tmp/alsa-build && \
- wget ftp://ftp.alsa-project.org/pub/lib/alsa-lib-1.1.4.1.tar.bz2 && \
- bzip2 -f -d alsa-lib-* && \
- tar xf alsa-lib-* && \
- cd alsa-lib-* && \
- ./configure --enable-static --disable-shared && \
- make clean && \
- make -j$(nproc) all && \
- make install
-
COPY . "${SRC}/adhd"
COPY cras/src/fuzz/build.sh "${SRC}/build.sh"
+RUN "${SRC}/adhd/cras/install_deps.sh"
RUN mkdir -p /etc/cras && cp "${SRC}/adhd/cras-config/dsp.ini.sample" /etc/cras
diff --git a/cras/src/libcras/cras_client.c b/cras/src/libcras/cras_client.c
index e5a1380..3fe631d 100644
--- a/cras/src/libcras/cras_client.c
+++ b/cras/src/libcras/cras_client.c
@@ -1403,7 +1403,6 @@
goto err_ret;
}
- samples_prot = 0;
if (stream->direction == CRAS_STREAM_OUTPUT)
samples_prot = PROT_WRITE;
else
@@ -2138,7 +2137,7 @@
rc = fill_socket_file((*client), conn_type);
if (rc < 0) {
- goto free_error;
+ goto free_server_event_fd;
}
rc = cras_file_wait_create((*client)->sock_file,
@@ -2171,10 +2170,11 @@
return 0;
free_error:
- if ((*client)->server_event_fd >= 0)
- close((*client)->server_event_fd);
cras_file_wait_destroy((*client)->sock_file_wait);
free((void *)(*client)->sock_file);
+free_server_event_fd:
+ if ((*client)->server_event_fd >= 0)
+ close((*client)->server_event_fd);
free_cond:
pthread_cond_destroy(&(*client)->stream_start_cond);
free_lock:
@@ -2632,6 +2632,21 @@
return debug_info;
}
+const struct main_thread_debug_info *
+cras_client_get_main_thread_debug_info(const struct cras_client *client)
+{
+ const struct main_thread_debug_info *debug_info;
+ int lock_rc;
+
+ lock_rc = server_state_rdlock(client);
+ if (lock_rc)
+ return 0;
+
+ debug_info = &client->server_state->main_thread_debug_info;
+ server_state_unlock(client, lock_rc);
+ return debug_info;
+}
+
const struct cras_bt_debug_info *
cras_client_get_bt_debug_info(const struct cras_client *client)
{
@@ -3168,6 +3183,20 @@
return len;
}
+int cras_client_update_main_thread_debug_info(
+ struct cras_client *client, void (*debug_info_cb)(struct cras_client *))
+{
+ struct cras_dump_main msg;
+
+ if (client == NULL)
+ return -EINVAL;
+ if (client->debug_info_callback != NULL)
+ return -EINVAL;
+ client->debug_info_callback = debug_info_cb;
+ cras_fill_dump_main(&msg);
+ return write_message_to_server(client, &msg.header);
+}
+
int cras_client_update_bt_debug_info(
struct cras_client *client, void (*debug_info_cb)(struct cras_client *))
{
@@ -3333,10 +3362,10 @@
{
struct cras_config_global_remix *msg;
int rc;
+ size_t nchan = (size_t)num_channels;
msg = (struct cras_config_global_remix *)malloc(
- sizeof(*msg) +
- num_channels * num_channels * sizeof(*coefficient));
+ sizeof(*msg) + nchan * nchan * sizeof(*coefficient));
cras_fill_config_global_remix_command(msg, num_channels, coefficient,
num_channels * num_channels);
rc = write_message_to_server(client, &msg->header);
diff --git a/cras/src/libcras/cras_client.h b/cras/src/libcras/cras_client.h
index c91ebe2..f7a18b5 100644
--- a/cras/src/libcras/cras_client.h
+++ b/cras/src/libcras/cras_client.h
@@ -477,6 +477,16 @@
int cras_client_update_audio_debug_info(struct cras_client *client,
void (*cb)(struct cras_client *));
+/* Asks the server to dump current main thread information.
+ * Args:
+ * client - The client from cras_client_create.
+ * cb - A function to call when the data is received.
+ * Returns:
+ * 0 on success, -EINVAL if the client isn't valid or isn't running.
+ */
+int cras_client_update_main_thread_debug_info(struct cras_client *client,
+ void (*cb)(struct cras_client *));
+
/* Asks the server to dump bluetooth debug information.
* Args:
* client - The client from cras_client_create.
@@ -856,6 +866,16 @@
const struct cras_bt_debug_info *
cras_client_get_bt_debug_info(const struct cras_client *client);
+/* Gets main thread debug info.
+ * Args:
+ * client - The client from cras_client_create.
+ * Returns:
+ * A pointer to the debug info. This info is updated and requested by
+ * calling cras_client_update_main_thread_debug_info.
+ */
+const struct main_thread_debug_info *
+cras_client_get_main_thread_debug_info(const struct cras_client *client);
+
/* Gets audio thread snapshot buffer.
*
* Requires that the connection to the server has been established.
diff --git a/cras/src/plc/cras_plc.c b/cras/src/plc/cras_plc.c
index ed42ae9..4bc9fb7 100644
--- a/cras/src/plc/cras_plc.c
+++ b/cras/src/plc/cras_plc.c
@@ -141,7 +141,7 @@
x2 += ((float)x[i]) * x[i];
y2 += ((float)y[i]) * y[i];
}
- return sum / sqrt(x2 * y2);
+ return sum / sqrtf(x2 * y2);
}
int pattern_match(int16_t *hist)
diff --git a/cras/src/plc/parse_sco.py b/cras/src/plc/parse_sco.py
new file mode 100755
index 0000000..c50df15
--- /dev/null
+++ b/cras/src/plc/parse_sco.py
@@ -0,0 +1,107 @@
+# -*- coding: utf-8 -*-
+# 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.
+"""
+A script to extract raw SCO RX packets from btsnoop.
+Use 'btmon -S' to dump SCO traffic from btsnoop file.
+Trim the btsnoop output to just the SCO traffic period.
+Then execute 'python parse-sco.py <btsnoop-output>'
+"""
+
+import atexit
+import binascii
+import os
+import re
+import sys
+
+
+class SCOParser:
+ """
+ Parser for grepping SCO packets
+ """
+
+ def __init__(self):
+ # On old releases, +CIEV: 4,1 indicates the start point of call session
+ # c 31 0d 0a 9a ..+CIEV: 4,1..
+ self.call_start_re = re.compile(r'.*?\+CIEV:\s4,(\d).*?')
+
+ # > SCO Data RX: Handle 257 flags 0x00 dlen 60 #13826 [hci0] 650.388305
+ # 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ # 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ # 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ # 00 00 00 00 00 00 00 00 00 00 00 00
+ self.sco_rx_re = re.compile(r'.*?SCO\sData\sRX.*?flags\s0x(\d+).*?')
+ self.sco_f = None
+ self.output_idx = 0
+ self.pk_count = 0
+ self.pl_count = 0
+
+ atexit.register(self._cleanup)
+
+ def _cleanup(self):
+ if self.sco_f is not None:
+ print(
+ "Current file contains %d packets (%d with erroneous status flag)" %
+ (self.pk_count, self.pl_count))
+ self.pk_count = 0
+ self.pl_count = 0
+ self.sco_f.close()
+
+ def _new_session(self):
+ if self.sco_f is not None:
+ close(self.sco_f)
+
+ new_file = "sco_file_%d" % self.output_idx
+ print("Record to %s" % new_file)
+ self.sco_f = open(new_file, 'wb')
+ self.output_idx += 1
+
+ return self.sco_f
+
+ def parse(self, filename):
+ if not os.path.exists(filename):
+ print("%s doesn't exist" % filename)
+ return
+
+ print("Start parsing %s" % filename)
+ parse_rx_data = 0
+ with open(filename, "r") as f:
+ for line in f.readlines():
+ if parse_rx_data > 0:
+ self.sco_f.write(binascii.unhexlify(''.join(line[:56].split())))
+ parse_rx_data = (parse_rx_data + 1) % 5
+
+ # Start a new session and output following SCO data to a new file
+ match = self.call_start_re.search(line)
+ if match and (1 == int(match.group(1))):
+ self._new_session()
+ continue
+
+ match = self.sco_rx_re.search(line)
+ if match:
+ if self.sco_f is None:
+ self._new_session()
+
+ self.pk_count += 1
+
+ status_flag = int(match.group(1))
+ hdr = ['01', str(status_flag) + '1', '3c']
+ if status_flag != 0:
+ self.pl_count += 1
+
+ self.sco_f.write(binascii.unhexlify(''.join(hdr)))
+ parse_rx_data = 1
+
+
+def main(argv):
+ if len(argv) < 1:
+ print("parse_sco.py [btsnoop.txt]")
+ return
+
+ p = SCOParser()
+ p.parse(argv[0])
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
diff --git a/cras/src/server/audio_thread.c b/cras/src/server/audio_thread.c
index 879b3c4..df713ca 100644
--- a/cras/src/server/audio_thread.c
+++ b/cras/src/server/audio_thread.c
@@ -835,6 +835,7 @@
while (1) {
struct timespec *wait_ts;
struct iodev_callback_list *iodev_cb;
+ int non_empty;
wait_ts = NULL;
thread->num_pollfds = 1;
@@ -844,6 +845,9 @@
&thread->open_devs[CRAS_STREAM_INPUT],
thread->remix_converter);
+ non_empty = dev_io_check_non_empty_state_transition(
+ thread->open_devs[CRAS_STREAM_OUTPUT]);
+
if (fill_next_sleep_interval(thread, &ts))
wait_ts = &ts;
@@ -884,7 +888,7 @@
log_busyloop(wait_ts);
ATLOG(atlog, AUDIO_THREAD_SLEEP, wait_ts ? wait_ts->tv_sec : 0,
- wait_ts ? wait_ts->tv_nsec : 0, 0);
+ wait_ts ? wait_ts->tv_nsec : 0, non_empty);
if (wait_ts)
check_busyloop(wait_ts);
@@ -894,6 +898,16 @@
rc = ppoll(thread->pollfds, thread->num_pollfds, wait_ts, NULL);
ATLOG(atlog, AUDIO_THREAD_WAKE, rc, 0, 0);
+
+ /* Handle callbacks registered by TRIGGER_WAKEUP */
+ DL_FOREACH (iodev_callbacks, iodev_cb) {
+ if (iodev_cb->trigger == TRIGGER_WAKEUP) {
+ ATLOG(atlog, AUDIO_THREAD_IODEV_CB, 0, 0, 0);
+ iodev_cb->cb(iodev_cb->cb_data, 0);
+ }
+ }
+
+ /* If there's no pollfd ready to handle. */
if (rc <= 0)
continue;
@@ -911,9 +925,6 @@
iodev_cb->events, 0);
iodev_cb->cb(iodev_cb->cb_data,
iodev_cb->pollfd->revents);
- } else if (iodev_cb->trigger == TRIGGER_WAKEUP) {
- ATLOG(atlog, AUDIO_THREAD_IODEV_CB, 0, 0, 0);
- iodev_cb->cb(iodev_cb->cb_data, 0);
}
}
}
diff --git a/cras/src/server/config/cras_board_config.c b/cras/src/server/config/cras_board_config.c
index f7dffcd..d04d626 100644
--- a/cras/src/server/config/cras_board_config.c
+++ b/cras/src/server/config/cras_board_config.c
@@ -12,7 +12,7 @@
static const int32_t DEFAULT_OUTPUT_BUFFER_SIZE = 512;
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 = 0;
+static const int32_t BLUETOOTH_WBS_ENABLED_INI_DEFAULT = 1;
#define CONFIG_NAME "board.ini"
#define DEFAULT_OUTPUT_BUF_SIZE_INI_KEY "output:default_output_buffer_size"
@@ -33,6 +33,7 @@
board_config->aec_supported = AEC_SUPPORTED_DEFAULT;
board_config->aec_group_id = AEC_GROUP_ID_DEFAULT;
board_config->ucm_ignore_suffix = NULL;
+ board_config->bt_wbs_enabled = BLUETOOTH_WBS_ENABLED_INI_DEFAULT;
if (config_path == NULL)
return;
diff --git a/cras/src/server/cras_alsa_io.c b/cras/src/server/cras_alsa_io.c
index 3e29ca1..792305b 100644
--- a/cras/src/server/cras_alsa_io.c
+++ b/cras/src/server/cras_alsa_io.c
@@ -113,7 +113,6 @@
* is_first - true if this is the first iodev on the card.
* fully_specified - true if this device and it's nodes were fully specified.
* That is, don't automatically create nodes for it.
- * jack_always_plugged - true if this node is always plugged even without jack.
* enable_htimestamp - True when the device's htimestamp is used.
* handle - Handle to the opened ALSA device.
* num_severe_underruns - Number of times we have run out of data badly.
@@ -149,7 +148,6 @@
enum CRAS_ALSA_CARD_TYPE card_type;
int is_first;
int fully_specified;
- int jack_always_plugged;
int enable_htimestamp;
snd_pcm_t *handle;
unsigned int num_severe_underruns;
@@ -798,7 +796,7 @@
ain ? ain->mixer_input : NULL);
/* For USB device without UCM config, not change a gain control. */
- if (ain->base.type == CRAS_NODE_TYPE_USB && !aio->ucm)
+ if (ain && ain->base.type == CRAS_NODE_TYPE_USB && !aio->ucm)
return;
/* Set hardware gain to 0dB if software gain is needed. */
@@ -1935,6 +1933,10 @@
struct alsa_io *aio = (struct alsa_io *)odev;
int rc;
+ /* Restart rate estimation because free run internval should not
+ * be included. */
+ cras_iodev_reset_rate_estimator(odev);
+
if (aio->free_running)
rc = adjust_appl_ptr_for_leaving_free_run(odev);
else
@@ -2053,7 +2055,6 @@
aio->is_first = is_first;
aio->handle = NULL;
aio->num_severe_underruns = 0;
- aio->jack_always_plugged = 0;
if (dev_name) {
aio->dev_name = strdup(dev_name);
if (!aio->dev_name)
@@ -2318,9 +2319,6 @@
} else if (input_node) {
input_node->jack = jack;
}
- } else if (aio->card_type == ALSA_CARD_TYPE_USB) {
- /* No jack is found, assume device is always plugged */
- aio->jack_always_plugged = 1;
}
return 0;
}
@@ -2328,6 +2326,7 @@
void alsa_iodev_ucm_complete_init(struct cras_iodev *iodev)
{
struct alsa_io *aio = (struct alsa_io *)iodev;
+ struct cras_ionode *node;
if (!iodev)
return;
@@ -2345,12 +2344,13 @@
/*
* Set plugged for the USB device per card when it appears if
- * there is no jack reporting plug status and the jack is set
- * to be always plugged.
+ * there is no jack reporting plug status
*/
- if (aio->card_type == ALSA_CARD_TYPE_USB && aio->jack_always_plugged &&
- !get_jack_from_node(iodev->active_node)) {
- cras_iodev_set_node_plugged(iodev->active_node, 1);
+ if (aio->card_type == ALSA_CARD_TYPE_USB) {
+ DL_FOREACH (iodev->nodes, node) {
+ if (!get_jack_from_node(node))
+ cras_iodev_set_node_plugged(node, 1);
+ }
}
set_default_hotword_model(iodev);
diff --git a/cras/src/server/cras_alsa_jack.c b/cras/src/server/cras_alsa_jack.c
index 58e5366..52a227e 100644
--- a/cras/src/server/cras_alsa_jack.c
+++ b/cras/src/server/cras_alsa_jack.c
@@ -719,6 +719,21 @@
return data->rc;
}
+/* Find ELD control for HDMI/DP gpio jack. */
+static snd_hctl_elem_t *find_eld_control_by_dev_index(snd_hctl_t *hctl,
+ unsigned int dev_idx)
+{
+ static const char eld_control_name[] = "ELD";
+ snd_ctl_elem_id_t *elem_id;
+
+ snd_ctl_elem_id_alloca(&elem_id);
+ snd_ctl_elem_id_clear(elem_id);
+ snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_PCM);
+ snd_ctl_elem_id_set_device(elem_id, dev_idx);
+ snd_ctl_elem_id_set_name(elem_id, eld_control_name);
+ return snd_hctl_find_elem(hctl, elem_id);
+}
+
/* Find GPIO jacks for this jack_list.
* Args:
* jack_list - Jack list to add to.
@@ -754,8 +769,16 @@
gpio_switch_list_for_each(gpio_switch_list_with_section, &data);
else
gpio_switch_list_for_each(gpio_switch_list_by_matching, &data);
- if (result_jack)
+ if (result_jack) {
*result_jack = data.result_jack;
+
+ /* Find ELD control for HDMI/DP gpio jack. */
+ if (*result_jack)
+ (*result_jack)->eld_control =
+ find_eld_control_by_dev_index(
+ jack_list->hctl,
+ jack_list->device_index);
+ }
return data.rc;
}
@@ -858,7 +881,6 @@
static const char *const input_jack_base_names[] = {
"Mic Jack",
};
- static const char eld_control_name[] = "ELD";
const char *const *jack_names;
unsigned int num_jack_names;
@@ -932,17 +954,9 @@
name = snd_hctl_elem_get_name(jack->elem);
if (!is_jack_hdmi_dp(name))
continue;
- for (elem = snd_hctl_first_elem(jack_list->hctl); elem != NULL;
- elem = snd_hctl_elem_next(elem)) {
- if (strcmp(snd_hctl_elem_get_name(elem),
- eld_control_name))
- continue;
- if (snd_hctl_elem_get_device(elem) !=
- jack_list->device_index)
- continue;
- jack->eld_control = elem;
- break;
- }
+
+ jack->eld_control = find_eld_control_by_dev_index(
+ jack_list->hctl, jack_list->device_index);
}
return 0;
@@ -968,7 +982,6 @@
struct ucm_section *section,
struct cras_alsa_jack **result_jack)
{
- static const char eld_control_name[] = "ELD";
snd_hctl_elem_t *elem;
snd_ctl_elem_id_t *elem_id;
struct cras_alsa_jack *jack;
@@ -1019,10 +1032,8 @@
return 0;
/* Look up ELD control. */
- snd_ctl_elem_id_set_name(elem_id, eld_control_name);
- elem = snd_hctl_find_elem(jack_list->hctl, elem_id);
- if (elem)
- jack->eld_control = elem;
+ jack->eld_control = find_eld_control_by_dev_index(
+ jack_list->hctl, jack_list->device_index);
return 0;
}
diff --git a/cras/src/server/cras_alsa_mixer.c b/cras/src/server/cras_alsa_mixer.c
index 99f4d61..1070557 100644
--- a/cras/src/server/cras_alsa_mixer.c
+++ b/cras/src/server/cras_alsa_mixer.c
@@ -417,7 +417,7 @@
int muted)
{
const struct mixer_control_element *elem = NULL;
- int rc;
+ int rc = -EINVAL;
if (!control)
return -EINVAL;
DL_FOREACH (control->elements, elem) {
@@ -959,9 +959,9 @@
if (!c->has_volume)
continue;
- mixer_control_set_dBFS(c, to_set);
- mixer_control_get_dBFS(c, &actual_dB);
- to_set -= actual_dB;
+ if (mixer_control_set_dBFS(c, to_set) == 0 &&
+ mixer_control_get_dBFS(c, &actual_dB) == 0)
+ to_set -= actual_dB;
}
/* Apply the rest to the output-specific control. */
if (cras_alsa_mixer_has_volume(mixer_output))
@@ -1002,9 +1002,9 @@
if (!c->has_volume)
continue;
- mixer_control_set_dBFS(c, to_set);
- mixer_control_get_dBFS(c, &actual_dB);
- to_set -= actual_dB;
+ if (mixer_control_set_dBFS(c, to_set) == 0 &&
+ mixer_control_get_dBFS(c, &actual_dB) == 0)
+ to_set -= actual_dB;
}
/* Apply the reset to input specific control */
diff --git a/cras/src/server/cras_alsa_plugin_io.c b/cras/src/server/cras_alsa_plugin_io.c
index 0d5f379..9c557a4 100644
--- a/cras/src/server/cras_alsa_plugin_io.c
+++ b/cras/src/server/cras_alsa_plugin_io.c
@@ -149,6 +149,16 @@
DL_APPEND(plugins, plugin);
+ ucm_sections = ucm_get_sections(plugin->ucm);
+ DL_FOREACH (ucm_sections, section) {
+ rc = cras_alsa_mixer_add_controls_in_section(plugin->mixer,
+ section);
+ if (rc)
+ syslog(LOG_ERR,
+ "Failed adding control to plugin,"
+ "section %s mixer_name %s",
+ section->name, section->mixer_name);
+ }
plugin->iodev =
alsa_iodev_create(0, card_name, 0, pcm_name, "", "",
ALSA_CARD_TYPE_USB, 1, /* is first */
@@ -156,7 +166,6 @@
plugin->hctl, direction, DUMMY_USB_VID,
DUMMY_USB_PID, DUMMY_USB_SERIAL_NUMBER);
- ucm_sections = ucm_get_sections(plugin->ucm);
DL_FOREACH (ucm_sections, section) {
if (section->dir != plugin->iodev->direction)
continue;
diff --git a/cras/src/server/cras_alsa_ucm.c b/cras/src/server/cras_alsa_ucm.c
index be0ce66..3782cb2 100644
--- a/cras/src/server/cras_alsa_ucm.c
+++ b/cras/src/server/cras_alsa_ucm.c
@@ -789,10 +789,125 @@
return -1;
}
+static const char *ucm_get_dir_for_device(struct cras_use_case_mgr *mgr,
+ const char *dev_name,
+ enum CRAS_STREAM_DIRECTION *dir)
+{
+ const char *pcm_name;
+
+ pcm_name = ucm_get_playback_device_name_for_dev(mgr, dev_name);
+
+ if (pcm_name) {
+ *dir = CRAS_STREAM_OUTPUT;
+ return pcm_name;
+ }
+
+ pcm_name = ucm_get_capture_device_name_for_dev(mgr, dev_name);
+ if (pcm_name) {
+ *dir = CRAS_STREAM_INPUT;
+ return pcm_name;
+ }
+
+ *dir = CRAS_STREAM_UNDEFINED;
+ return NULL;
+}
+
+static int ucm_parse_device_section(struct cras_use_case_mgr *mgr,
+ const char *dev,
+ struct ucm_section **sections)
+{
+ enum CRAS_STREAM_DIRECTION dir;
+ int dev_idx = -1;
+ int dependent_dev_idx = -1;
+ const char *jack_name = NULL;
+ const char *jack_type = NULL;
+ const char *jack_dev = NULL;
+ const char *jack_control = NULL;
+ const char *mixer_name = NULL;
+ struct mixer_name *m_name;
+ int rc = 0;
+ const char *pcm_name;
+ const char *dependent_dev_name = NULL;
+ struct ucm_section *dev_sec;
+ const char *dev_name;
+
+ dev_name = strdup(dev);
+ if (!dev_name)
+ return 0;
+
+ pcm_name = ucm_get_dir_for_device(mgr, dev_name, &dir);
+
+ if (pcm_name)
+ dev_idx = get_device_index_from_target(pcm_name);
+
+ if (dir == CRAS_STREAM_UNDEFINED) {
+ syslog(LOG_ERR,
+ "UCM configuration for device '%s' missing"
+ " PlaybackPCM or CapturePCM definition.",
+ dev_name);
+ rc = -EINVAL;
+ goto error_cleanup;
+ }
+
+ dependent_dev_name =
+ ucm_get_dependent_device_name_for_dev(mgr, dev_name);
+ if (dependent_dev_name) {
+ dependent_dev_idx =
+ get_device_index_from_target(dependent_dev_name);
+ }
+
+ jack_dev = ucm_get_jack_dev_for_dev(mgr, dev_name);
+ jack_control = ucm_get_jack_control_for_dev(mgr, dev_name);
+ if (dir == CRAS_STREAM_OUTPUT)
+ mixer_name = ucm_get_playback_mixer_elem_for_dev(mgr, dev_name);
+ else if (dir == CRAS_STREAM_INPUT)
+ mixer_name = ucm_get_capture_mixer_elem_for_dev(mgr, dev_name);
+
+ if (jack_dev) {
+ jack_name = jack_dev;
+ jack_type = "gpio";
+ } else if (jack_control) {
+ jack_name = jack_control;
+ jack_type = "hctl";
+ }
+
+ dev_sec = ucm_section_create(dev_name, pcm_name, dev_idx,
+ dependent_dev_idx, dir, jack_name,
+ jack_type);
+
+ if (!dev_sec) {
+ syslog(LOG_ERR, "Failed to allocate memory.");
+ rc = -ENOMEM;
+ goto error_cleanup;
+ }
+
+ dev_sec->jack_switch = ucm_get_jack_switch_for_dev(mgr, dev_name);
+
+ if (mixer_name) {
+ rc = ucm_section_set_mixer_name(dev_sec, mixer_name);
+ if (rc)
+ goto error_cleanup;
+ }
+
+ m_name = ucm_get_mixer_names(mgr, dev_name, coupled_mixers, dir,
+ MIXER_NAME_VOLUME);
+ ucm_section_concat_coupled(dev_sec, m_name);
+
+ DL_APPEND(*sections, dev_sec);
+ ucm_section_dump(dev_sec);
+error_cleanup:
+ free((void *)dev_name);
+ free((void *)dependent_dev_name);
+ free((void *)jack_dev);
+ free((void *)jack_control);
+ free((void *)mixer_name);
+ free((void *)pcm_name);
+ return rc;
+}
+
struct ucm_section *ucm_get_sections(struct cras_use_case_mgr *mgr)
{
struct ucm_section *sections = NULL;
- struct ucm_section *dev_sec;
const char **list;
int num_devs;
int i;
@@ -806,117 +921,17 @@
/* snd_use_case_get_list fills list with pairs of device name and
* comment, so device names are in even-indexed elements. */
- const char *dev_name;
for (i = 0; i < num_devs; i += 2) {
- enum CRAS_STREAM_DIRECTION dir = CRAS_STREAM_UNDEFINED;
- int dev_idx = -1;
- int dependent_dev_idx = -1;
- const char *jack_name = NULL;
- const char *jack_type = NULL;
- const char *jack_dev;
- const char *jack_control;
- const char *mixer_name;
- struct mixer_name *m_name;
- int rc;
- const char *pcm_name;
- const char *dependent_dev_name;
-
- dev_name = strdup(list[i]);
- if (!dev_name)
- continue;
-
- pcm_name = ucm_get_playback_device_name_for_dev(mgr, dev_name);
- if (pcm_name) {
- dir = CRAS_STREAM_OUTPUT;
- } else {
- pcm_name = ucm_get_capture_device_name_for_dev(
- mgr, dev_name);
- if (pcm_name)
- dir = CRAS_STREAM_INPUT;
+ if (ucm_parse_device_section(mgr, list[i], §ions) < 0) {
+ ucm_section_free_list(sections);
+ sections = NULL;
+ break;
}
- if (pcm_name)
- dev_idx = get_device_index_from_target(pcm_name);
-
- if (dir == CRAS_STREAM_UNDEFINED) {
- syslog(LOG_ERR,
- "UCM configuration for device '%s' missing"
- " PlaybackPCM or CapturePCM definition.",
- dev_name);
- free((void *)pcm_name);
- goto error_cleanup;
- }
-
- dependent_dev_name =
- ucm_get_dependent_device_name_for_dev(mgr, dev_name);
- if (dependent_dev_name) {
- dependent_dev_idx = get_device_index_from_target(
- dependent_dev_name);
- free((void *)dependent_dev_name);
- }
-
- jack_dev = ucm_get_jack_dev_for_dev(mgr, dev_name);
- jack_control = ucm_get_jack_control_for_dev(mgr, dev_name);
- if (dir == CRAS_STREAM_OUTPUT)
- mixer_name = ucm_get_playback_mixer_elem_for_dev(
- mgr, dev_name);
- else if (dir == CRAS_STREAM_INPUT)
- mixer_name = ucm_get_capture_mixer_elem_for_dev(
- mgr, dev_name);
-
- if (jack_dev) {
- jack_name = jack_dev;
- jack_type = "gpio";
- } else if (jack_control) {
- jack_name = jack_control;
- jack_type = "hctl";
- }
-
- dev_sec = ucm_section_create(dev_name, pcm_name, dev_idx,
- dependent_dev_idx, dir, jack_name,
- jack_type);
- if (pcm_name)
- free((void *)pcm_name);
- if (jack_dev)
- free((void *)jack_dev);
- if (jack_control)
- free((void *)jack_control);
-
- if (!dev_sec) {
- syslog(LOG_ERR, "Failed to allocate memory.");
- if (mixer_name)
- free((void *)mixer_name);
- goto error_cleanup;
- }
-
- dev_sec->jack_switch =
- ucm_get_jack_switch_for_dev(mgr, dev_name);
-
- if (mixer_name) {
- rc = ucm_section_set_mixer_name(dev_sec, mixer_name);
- free((void *)mixer_name);
- if (rc)
- goto error_cleanup;
- }
-
- m_name = ucm_get_mixer_names(mgr, dev_name, coupled_mixers, dir,
- MIXER_NAME_VOLUME);
- ucm_section_concat_coupled(dev_sec, m_name);
-
- DL_APPEND(sections, dev_sec);
- ucm_section_dump(dev_sec);
- free((void *)dev_name);
}
if (num_devs > 0)
snd_use_case_free_list(list, num_devs);
return sections;
-
-error_cleanup:
- if (num_devs > 0)
- snd_use_case_free_list(list, num_devs);
- ucm_section_free_list(sections);
- free((void *)dev_name);
- return NULL;
}
char *ucm_get_hotword_models(struct cras_use_case_mgr *mgr)
diff --git a/cras/src/server/cras_alsa_ucm_section.c b/cras/src/server/cras_alsa_ucm_section.c
index 205a189..d4df8c7 100644
--- a/cras/src/server/cras_alsa_ucm_section.c
+++ b/cras/src/server/cras_alsa_ucm_section.c
@@ -13,16 +13,11 @@
static void ucm_section_free(struct ucm_section *section)
{
- if (section->name)
- free((void *)section->name);
- if (section->pcm_name)
- free((void *)section->pcm_name);
- if (section->jack_name)
- free((void *)section->jack_name);
- if (section->jack_type)
- free((void *)section->jack_type);
- if (section->mixer_name)
- free((void *)section->mixer_name);
+ free((void *)section->name);
+ free((void *)section->pcm_name);
+ free((void *)section->jack_name);
+ free((void *)section->jack_type);
+ free((void *)section->mixer_name);
mixer_name_free(section->coupled);
free(section);
}
diff --git a/cras/src/server/cras_bt_device.c b/cras/src/server/cras_bt_device.c
index 06b9771..0607ac8 100644
--- a/cras/src/server/cras_bt_device.c
+++ b/cras/src/server/cras_bt_device.c
@@ -1036,12 +1036,23 @@
struct sco_options so;
socklen_t len = sizeof(so);
struct cras_bt_adapter *adapter;
+ uint32_t wbs_pkt_len = 0;
+ socklen_t optlen = sizeof(wbs_pkt_len);
adapter = cras_bt_adapter_get(device->adapter_obj_path);
if (cras_bt_adapter_on_usb(adapter)) {
- return (codec == HFP_CODEC_ID_MSBC) ? USB_MSBC_PKT_SIZE :
- USB_CVSD_PKT_SIZE;
+ if (codec == HFP_CODEC_ID_MSBC) {
+ /* BT_SNDMTU and BT_RCVMTU return the same value. */
+ if (getsockopt(sco_socket, SOL_BLUETOOTH, BT_SNDMTU,
+ &wbs_pkt_len, &optlen))
+ syslog(LOG_ERR, "Failed to get BT_SNDMTU");
+
+ return (wbs_pkt_len > 0) ? wbs_pkt_len :
+ USB_MSBC_PKT_SIZE;
+ } else {
+ return USB_CVSD_PKT_SIZE;
+ }
}
/* For non-USB cases, query the SCO MTU from driver. */
diff --git a/cras/src/server/cras_bt_io.c b/cras/src/server/cras_bt_io.c
index 2062b9f..3cffe14 100644
--- a/cras/src/server/cras_bt_io.c
+++ b/cras/src/server/cras_bt_io.c
@@ -522,7 +522,7 @@
* point it to the first profile dev. */
active = (struct bt_node *)calloc(1, sizeof(*active));
if (!active)
- return NULL;
+ goto error;
active->base.dev = iodev;
active->base.idx = btio->next_node_id++;
active->base.type = dev->active_node->type;
diff --git a/cras/src/server/cras_bt_transport.c b/cras/src/server/cras_bt_transport.c
index d137dbb..402cd75 100644
--- a/cras/src/server/cras_bt_transport.c
+++ b/cras/src/server/cras_bt_transport.c
@@ -287,17 +287,24 @@
} else if (type == DBUS_TYPE_OBJECT_PATH) {
const char *obj_path;
- /* Property: object Device [readonly] */
- dbus_message_iter_get_basic(&variant_iter, &obj_path);
- transport->device = cras_bt_device_get(obj_path);
- if (!transport->device) {
- syslog(LOG_ERR,
- "Device %s not found at update"
- "transport properties",
- obj_path);
- transport->device = cras_bt_device_create(
- transport->conn, obj_path);
- cras_bt_transport_update_device(transport);
+ if (strcmp(key, "Device") == 0) {
+ /* Property: object Device [readonly] */
+ dbus_message_iter_get_basic(&variant_iter,
+ &obj_path);
+ transport->device =
+ cras_bt_device_get(obj_path);
+ if (!transport->device) {
+ syslog(LOG_ERR,
+ "Device %s not found at update "
+ "transport properties",
+ obj_path);
+ transport->device =
+ cras_bt_device_create(
+ transport->conn,
+ obj_path);
+ cras_bt_transport_update_device(
+ transport);
+ }
}
} else if (strcmp(dbus_message_iter_get_signature(&variant_iter),
"ay") == 0 &&
@@ -324,6 +331,7 @@
dbus_message_iter_get_basic(&variant_iter, &volume);
transport->volume = volume;
+ BTLOG(btlog, BT_TRANSPORT_UPDATE_VOLUME, volume, 0);
cras_bt_transport_update_device(transport);
}
@@ -377,6 +385,7 @@
DBusMessageIter message_iter, variant;
DBusPendingCall *pending_call;
+ BTLOG(btlog, BT_TRANSPORT_SET_VOLUME, volume, 0);
method_call =
dbus_message_new_method_call(BLUEZ_SERVICE,
transport->object_path,
diff --git a/cras/src/server/cras_control_rclient.c b/cras/src/server/cras_control_rclient.c
index 2b30fe5..3906a23 100644
--- a/cras/src/server/cras_control_rclient.c
+++ b/cras/src/server/cras_control_rclient.c
@@ -15,6 +15,7 @@
#include "cras_dsp.h"
#include "cras_iodev.h"
#include "cras_iodev_list.h"
+#include "cras_main_thread_log.h"
#include "cras_messages.h"
#include "cras_observer.h"
#include "cras_rclient.h"
@@ -401,6 +402,19 @@
case CRAS_SERVER_GET_ATLOG_FD:
get_atlog_fd(client);
break;
+ case CRAS_SERVER_DUMP_MAIN: {
+ struct cras_client_audio_debug_info_ready msg;
+ struct cras_server_state *state;
+
+ state = cras_system_state_get_no_lock();
+ memcpy(&state->main_thread_debug_info.main_log, main_log,
+ sizeof(struct main_thread_event_log));
+
+ cras_fill_client_audio_debug_info_ready(&msg);
+ client->ops->send_message_to_client(client, &msg.header, NULL,
+ 0);
+ break;
+ }
case CRAS_SERVER_DUMP_BT: {
struct cras_client_audio_debug_info_ready msg;
struct cras_server_state *state;
@@ -463,7 +477,7 @@
m->num_channels > CRAS_MAX_REMIX_CHANNELS)
return -EINVAL;
const size_t coefficient_len =
- m->num_channels * m->num_channels;
+ (size_t)m->num_channels * (size_t)m->num_channels;
const size_t size_with_coefficients =
sizeof(*m) +
coefficient_len * sizeof(m->coefficient[0]);
diff --git a/cras/src/server/cras_dbus_control.c b/cras/src/server/cras_dbus_control.c
index 0a60c67..628ec22 100644
--- a/cras/src/server/cras_dbus_control.c
+++ b/cras/src/server/cras_dbus_control.c
@@ -17,6 +17,7 @@
#include "cras_dbus_util.h"
#include "cras_hfp_ag_profile.h"
#include "cras_iodev_list.h"
+#include "cras_main_thread_log.h"
#include "cras_observer.h"
#include "cras_system_state.h"
#include "cras_utf8.h"
@@ -92,9 +93,6 @@
" <method name=\"RemoveActiveOutputNode\">\n" \
" <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n" \
" </method>\n" \
- " <method name=\"SetNextHandsfreeProfile\">\n" \
- " <arg name=\"toggle\" type=\"b\" direction=\"in\"/>\n" \
- " </method>\n" \
" <method name=\"SetFixA2dpPacketSize\">\n" \
" <arg name=\"toggle\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
@@ -133,6 +131,8 @@
" <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" \
@@ -369,6 +369,7 @@
return rc;
cras_system_set_user_mute(new_mute);
+ MAINLOG(main_log, MAIN_THREAD_SET_OUTPUT_USER_MUTE, new_mute, 0, 0);
send_empty_reply(conn, message);
@@ -497,10 +498,8 @@
dbus_uint64_t id;
const char *dev_name = dev->name;
dbus_uint64_t stable_dev_id = node->stable_id;
- dbus_uint32_t max_supported_channels = dev->max_supported_channels;
const char *node_type = node->type;
const char *node_name = node->name;
- const char *mic_positions = node->mic_positions;
dbus_bool_t active;
dbus_uint64_t plugged_time = node->plugged_time.tv_sec * 1000000ULL +
node->plugged_time.tv_usec;
@@ -540,19 +539,12 @@
if (!append_key_value(&dict, "StableDeviceId", DBUS_TYPE_UINT64,
DBUS_TYPE_UINT64_AS_STRING, &stable_dev_id))
return FALSE;
- if (!append_key_value(&dict, "MaxSupportedChannels", DBUS_TYPE_UINT32,
- DBUS_TYPE_UINT32_AS_STRING,
- &max_supported_channels))
- return FALSE;
if (!append_key_value(&dict, "Type", DBUS_TYPE_STRING,
DBUS_TYPE_STRING_AS_STRING, &node_type))
return FALSE;
if (!append_key_value(&dict, "Name", DBUS_TYPE_STRING,
DBUS_TYPE_STRING_AS_STRING, &node_name))
return FALSE;
- if (!append_key_value(&dict, "MicPositions", DBUS_TYPE_STRING,
- DBUS_TYPE_STRING_AS_STRING, &mic_positions))
- return FALSE;
if (!append_key_value(&dict, "Active", DBUS_TYPE_BOOLEAN,
DBUS_TYPE_BOOLEAN_AS_STRING, &active))
return FALSE;
@@ -732,28 +724,6 @@
return DBUS_HANDLER_RESULT_HANDLED;
}
-static DBusHandlerResult handle_set_next_handsfree_profile(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;
-
- /* Change HFP version to register with BlueZ and the
- * wbs enabled flag for codec negotiation in SLC.
- */
- cras_hfp_ag_profile_next_handsfree(conn, enabled);
- cras_system_set_bt_wbs_enabled(enabled);
-
- send_empty_reply(conn, message);
-
- return DBUS_HANDLER_RESULT_HANDLED;
-}
-
static DBusHandlerResult handle_set_fix_a2dp_packet_size(DBusConnection *conn,
DBusMessage *message,
void *arg)
@@ -1004,6 +974,17 @@
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)
@@ -1095,9 +1076,6 @@
return handle_rm_active_node(conn, message, arg,
CRAS_STREAM_OUTPUT);
} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
- "SetNextHandsfreeProfile")) {
- return handle_set_next_handsfree_profile(conn, message, arg);
- } else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
"SetFixA2dpPacketSize")) {
return handle_set_fix_a2dp_packet_size(conn, message, arg);
} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
@@ -1138,6 +1116,9 @@
} 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;
diff --git a/cras/src/server/cras_dsp_pipeline.c b/cras/src/server/cras_dsp_pipeline.c
index 17b5de5..9e492ac 100644
--- a/cras/src/server/cras_dsp_pipeline.c
+++ b/cras/src/server/cras_dsp_pipeline.c
@@ -435,6 +435,7 @@
if (rc < 0) {
syslog(LOG_ERR, "failed to construct pipeline");
+ cras_dsp_pipeline_free(pipeline);
return NULL;
}
@@ -524,6 +525,17 @@
peak_buf = MAX(peak_buf, need_buf);
}
}
+ /*
+ * cras_dsp_pipeline_create creates pipeline with source and sink and it
+ * makes sure all ports could be accessed from some sources, which means
+ * that there is at least one source with out > 0 and in == 0.
+ * This will give us peak_buf > 0 in the previous calculation.
+ */
+ if (peak_buf <= 0) {
+ syslog(LOG_ERR, "peak_buf = %d, which must be greater than 0.",
+ peak_buf);
+ return -1;
+ }
/* then allocate the buffers */
pipeline->peak_buf = peak_buf;
diff --git a/cras/src/server/cras_fmt_conv.c b/cras/src/server/cras_fmt_conv.c
index 71ee366..478452d 100644
--- a/cras/src/server/cras_fmt_conv.c
+++ b/cras/src/server/cras_fmt_conv.c
@@ -384,11 +384,12 @@
conv->ch_conv_mtx = cras_channel_conv_matrix_create(in, out);
if (conv->ch_conv_mtx == NULL) {
syslog(LOG_ERR,
- "Failed to create channel conversion matrix");
- cras_fmt_conv_destroy(&conv);
- return NULL;
+ "Failed to create channel conversion matrix."
+ "Fallback to default_all_to_all.");
+ conv->channel_converter = default_all_to_all;
+ } else {
+ conv->channel_converter = convert_channels;
}
- conv->channel_converter = convert_channels;
}
/* Set up sample rate conversion. */
if (in->frame_rate != out->frame_rate) {
diff --git a/cras/src/server/cras_fmt_conv_ops.c b/cras/src/server/cras_fmt_conv_ops.c
index d75ce1d..87358af 100644
--- a/cras/src/server/cras_fmt_conv_ops.c
+++ b/cras/src/server/cras_fmt_conv_ops.c
@@ -331,14 +331,22 @@
unsigned int in_ch, out_ch, i;
const int16_t *in = (const int16_t *)_in;
int16_t *out = (int16_t *)_out;
+ int32_t sum;
- memset(out, 0, in_frames * cras_get_format_bytes(out_fmt));
- for (out_ch = 0; out_ch < num_out_ch; out_ch++) {
+ for (i = 0; i < in_frames; i++) {
+ sum = 0;
for (in_ch = 0; in_ch < num_in_ch; in_ch++) {
- for (i = 0; i < in_frames; i++) {
- out[out_ch + i * num_out_ch] +=
- in[in_ch + i * num_in_ch] / num_in_ch;
- }
+ sum += (int32_t)in[in_ch + i * num_in_ch];
+ }
+ /*
+ * 1. Divide `int32_t` by `size_t` without an explicit
+ * conversion will generate corrupted results.
+ * 2. After the division, `sum` should be in the range of
+ * int16_t. No clipping is needed.
+ */
+ sum /= (int32_t)num_in_ch;
+ for (out_ch = 0; out_ch < num_out_ch; out_ch++) {
+ out[out_ch + i * num_out_ch] = (int16_t)sum;
}
}
return in_frames;
diff --git a/cras/src/server/cras_hfp_ag_profile.c b/cras/src/server/cras_hfp_ag_profile.c
index b433635..8cf4a43 100644
--- a/cras/src/server/cras_hfp_ag_profile.c
+++ b/cras/src/server/cras_hfp_ag_profile.c
@@ -20,12 +20,12 @@
#include "cras_server_metrics.h"
#include "cras_system_state.h"
#include "cras_iodev_list.h"
+#include "cras_observer.h"
#include "utlist.h"
#define HFP_AG_PROFILE_NAME "Hands-Free Voice gateway"
#define HFP_AG_PROFILE_PATH "/org/chromium/Cras/Bluetooth/HFPAG"
-#define HFP_VERSION_1_5 0x0105
-#define HFP_VERSION_1_7 0x0107
+#define HFP_VERSION 0x0107
#define HSP_AG_PROFILE_NAME "Headset Voice gateway"
#define HSP_AG_PROFILE_PATH "/org/chromium/Cras/Bluetooth/HSPAG"
#define HSP_VERSION_1_2 0x0102
@@ -76,8 +76,7 @@
#define BSRF_SUPPORTED_FEATURES (AG_ENHANCED_CALL_STATUS | AG_HF_INDICATORS)
/* The "SupportedFeatures" attribute value of HFP AG service record in CRAS. */
-#define SDP_SUPPORTED_FEATURES 0
-#define SDP_SUPPORTED_FEATURES_1_7 FEATURES_AG_WIDE_BAND_SPEECH
+#define SDP_SUPPORTED_FEATURES FEATURES_AG_WIDE_BAND_SPEECH
/* Object representing the audio gateway role for HFP/HSP.
* Members:
@@ -269,7 +268,7 @@
* control whether to turn on WBS feature.
*/
ag_features = BSRF_SUPPORTED_FEATURES;
- if (cras_system_get_bt_wbs_enabled() &&
+ if (cras_system_get_bt_wbs_enabled() && adapter &&
cras_bt_adapter_wbs_supported(adapter))
ag_features |= AG_CODEC_NEGOTIATION;
@@ -289,10 +288,10 @@
DL_FOREACH (connected_ags, ag) {
if (ag->slc_handle && ag->device == device) {
- destroy_audio_gateway(ag);
cras_bt_device_notify_profile_dropped(
ag->device,
CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE);
+ destroy_audio_gateway(ag);
}
}
}
@@ -305,7 +304,7 @@
.name = HFP_AG_PROFILE_NAME,
.object_path = HFP_AG_PROFILE_PATH,
.uuid = HFP_AG_UUID,
- .version = HFP_VERSION_1_5,
+ .version = HFP_VERSION,
.role = NULL,
.features = SDP_SUPPORTED_FEATURES,
.record = NULL,
@@ -315,30 +314,6 @@
.cancel = cras_hfp_ag_cancel
};
-int cras_hfp_ag_profile_next_handsfree(DBusConnection *conn, bool enabled)
-{
- unsigned int target_version = HFP_VERSION_1_5;
- unsigned int target_feature = SDP_SUPPORTED_FEATURES;
-
- if (enabled) {
- target_version = HFP_VERSION_1_7;
- target_feature = SDP_SUPPORTED_FEATURES_1_7;
- }
-
- if (cras_hfp_ag_profile.version == target_version)
- return 0;
-
- cras_bt_unregister_profile(conn, &cras_hfp_ag_profile);
- cras_bt_rm_profile(conn, &cras_hfp_ag_profile);
-
- cras_hfp_ag_profile.features = target_feature;
- cras_hfp_ag_profile.version = target_version;
-
- cras_bt_add_profile(conn, &cras_hfp_ag_profile);
- cras_bt_register_profile(conn, &cras_hfp_ag_profile);
- return 0;
-}
-
int cras_hfp_ag_profile_create(DBusConnection *conn)
{
return cras_bt_add_profile(conn, &cras_hfp_ag_profile);
@@ -385,9 +360,9 @@
DL_FOREACH (connected_ags, ag) {
if (ag->slc_handle && ag->device == device) {
- destroy_audio_gateway(ag);
cras_bt_device_notify_profile_dropped(
ag->device, CRAS_BT_DEVICE_PROFILE_HSP_HEADSET);
+ destroy_audio_gateway(ag);
}
}
}
@@ -478,6 +453,19 @@
return NULL;
}
+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 6a411b1..fb58efb 100644
--- a/cras/src/server/cras_hfp_ag_profile.h
+++ b/cras/src/server/cras_hfp_ag_profile.h
@@ -26,12 +26,6 @@
struct hfp_slc_handle;
-/* Re-registers HFP to the next version. Currently on HFP 1.5 and next is 1.7.
- * Args:
- * enabled - True to register HFP 1.7 otherwise to HFP 1.5
- */
-int cras_hfp_ag_profile_next_handsfree(DBusConnection *conn, bool enabled);
-
/* Adds a profile instance for HFP AG (Hands-Free Profile Audio Gateway). */
int cras_hfp_ag_profile_create(DBusConnection *conn);
@@ -59,4 +53,8 @@
/* Gets the SLC handle for given cras_bt_device. */
struct hfp_slc_handle *cras_hfp_ag_get_slc(struct cras_bt_device *device);
+/* 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 2ca7901..532b6c4 100644
--- a/cras/src/server/cras_hfp_alsa_iodev.c
+++ b/cras/src/server/cras_hfp_alsa_iodev.c
@@ -52,18 +52,7 @@
static int hfp_alsa_update_supported_formats(struct cras_iodev *iodev)
{
- struct hfp_alsa_io *hfp_alsa_io = (struct hfp_alsa_io *)iodev;
- struct cras_iodev *aio = hfp_alsa_io->aio;
-
/* 16 bit, mono, 8kHz (narrow band speech); */
- free(aio->format);
- aio->format = malloc(sizeof(struct cras_audio_format));
- if (!aio->format)
- return -ENOMEM;
- aio->format->format = SND_PCM_FORMAT_S16_LE;
- aio->format->frame_rate = 8000;
- aio->format->num_channels = 1;
-
free(iodev->supported_rates);
iodev->supported_rates = malloc(2 * sizeof(*iodev->supported_rates));
if (!iodev->supported_rates)
@@ -96,6 +85,15 @@
struct cras_iodev *aio = hfp_alsa_io->aio;
int rc;
+ /* Fill back the format iodev is using. */
+ if (aio->format == NULL) {
+ aio->format = (struct cras_audio_format *)malloc(
+ sizeof(*aio->format));
+ if (!aio->format)
+ return -ENOMEM;
+ *aio->format = *iodev->format;
+ }
+
rc = aio->configure_dev(aio);
if (rc) {
syslog(LOG_ERR, "Failed to configure aio: %d\n", rc);
diff --git a/cras/src/server/cras_hfp_info.c b/cras/src/server/cras_hfp_info.c
index bc916d7..550de58 100644
--- a/cras/src/server/cras_hfp_info.c
+++ b/cras/src/server/cras_hfp_info.c
@@ -40,11 +40,21 @@
/* For one mSBC 1 compressed wideband audio channel the HCI packets will
* be 3 octets of HCI header + 60 octets of data. */
#define MSBC_PKT_SIZE 60
-#define WRITE_BUF_SIZE_BYTES MSBC_PKT_SIZE
-#define HCI_SCO_PKT_SIZE (MSBC_PKT_SIZE)
#define H2_HEADER_0 0x01
+/* Supported HCI SCO packet sizes. The wideband speech mSBC frame parsing
+ * code ties to limited packet size values. Specifically list them out
+ * to check against when setting packet size.
+ *
+ * Temp buffer size should be set to least common multiple of HCI SCO packet
+ * size and MSBC_PKT_SIZE for optimizing buffer copy.
+ * To add a new supported packet size value, add corresponding entry to the
+ * lists, test the read/write msbc code, and fix the code if needed.
+ */
+static const size_t wbs_supported_packet_size[] = { 60, 24, 0 };
+static const size_t wbs_hci_sco_buffer_size[] = { 60, 120, 0 };
+
/* Second octet of H2 header is composed by 4 bits fixed 0x8 and 4 bits
* sequence number 0000, 0011, 1100, 1111. */
static const uint8_t h2_header_frames_count[] = { 0x08, 0x38, 0xc8, 0xf8 };
@@ -70,11 +80,19 @@
* read_cb - Callback to call when SCO socket can read. It returns the
* number of PCM bytes read.
* write_cb - Callback to call when SCO socket can write.
- * hci_sco_buf - Buffer to read one HCI SCO packet.
+ * write_buf - Temp buffer for writeing HCI SCO packet in wideband.
+ * read_buf - Temp buffer for reading HCI SCO packet in wideband.
* input_format_bytes - The audio format bytes for input device. 0 means
* there is no input device for the hfp_info.
* output_format_bytes - The audio format bytes for output device. 0 means
* there is no output device for the hfp_info.
+ * write_wp - Write pointer of write_buf.
+ * write_rp - Read pointer of write_buf.
+ * read_wp - Write pointer of read_buf.
+ * read_rp - Read pointer of read_buf.
+ * read_align_cb - Callback used to align mSBC frame reading with read buf.
+ * msbc_read_current_corrupted - Flag to mark if the current mSBC frame
+ * read is corrupted.
*/
struct hfp_info {
int fd;
@@ -91,10 +109,16 @@
unsigned int msbc_num_lost_frames;
int (*read_cb)(struct hfp_info *info);
int (*write_cb)(struct hfp_info *info);
- uint8_t write_buf[WRITE_BUF_SIZE_BYTES];
- uint8_t hci_sco_buf[HCI_SCO_PKT_SIZE];
+ uint8_t *write_buf;
+ uint8_t *read_buf;
size_t input_format_bytes;
size_t output_format_bytes;
+ size_t write_wp;
+ size_t write_rp;
+ size_t read_wp;
+ size_t read_rp;
+ int (*read_align_cb)(uint8_t *buf);
+ bool msbc_read_current_corrupted;
};
int hfp_info_add_iodev(struct hfp_info *info,
@@ -243,6 +267,9 @@
uint8_t *samples;
uint8_t *wp;
+ if (info->write_rp + info->packet_size <= info->write_wp)
+ goto msbc_send_again;
+
/* Make sure there are MSBC_CODE_SIZE bytes to encode. */
samples = buf_read_pointer_size(info->playback_buf, &pcm_avail);
if (pcm_avail < MSBC_CODE_SIZE) {
@@ -261,31 +288,41 @@
}
/* Encode the next MSBC_CODE_SIZE of bytes. */
- wp = info->write_buf;
+ wp = info->write_buf + info->write_wp;
wp[0] = H2_HEADER_0;
wp[1] = h2_header_frames_count[info->msbc_num_out_frames % 4];
pcm_encoded = info->msbc_write->encode(
info->msbc_write, samples, pcm_avail, wp + MSBC_H2_HEADER_LEN,
- WRITE_BUF_SIZE_BYTES - MSBC_H2_HEADER_LEN, &encoded);
+ MSBC_PKT_SIZE - MSBC_H2_HEADER_LEN, &encoded);
if (pcm_encoded < 0) {
syslog(LOG_ERR, "msbc encoding err: %s", strerror(pcm_encoded));
return pcm_encoded;
}
buf_increment_read(info->playback_buf, pcm_encoded);
pcm_avail -= pcm_encoded;
+ info->write_wp += MSBC_PKT_SIZE;
+ info->msbc_num_out_frames++;
+
+ if (info->write_rp + info->packet_size > info->write_wp)
+ return 0;
msbc_send_again:
- err = send(info->fd, info->write_buf, MSBC_PKT_SIZE, 0);
+ err = send(info->fd, info->write_buf + info->write_rp,
+ info->packet_size, 0);
if (err < 0) {
if (errno == EINTR)
goto msbc_send_again;
return err;
}
- if (err != MSBC_PKT_SIZE) {
+ if (err != (int)info->packet_size) {
syslog(LOG_ERR, "Partially write %d bytes for mSBC", err);
return -1;
}
- info->msbc_num_out_frames++;
+ info->write_rp += info->packet_size;
+ if (info->write_rp == info->write_wp) {
+ info->write_rp = 0;
+ info->write_wp = 0;
+ }
return err;
}
@@ -396,6 +433,16 @@
return decoded;
}
+/* Checks if mSBC frame header aligns with the beginning of buffer. */
+static int msbc_frame_align(uint8_t *buf)
+{
+ if ((buf[0] != H2_HEADER_0) || (buf[2] != MSBC_SYNC_WORD)) {
+ syslog(LOG_DEBUG, "Waiting for valid mSBC frame head");
+ return 0;
+ }
+ return 1;
+}
+
int hfp_read_msbc(struct hfp_info *info)
{
int err = 0;
@@ -411,14 +458,15 @@
struct iovec iov;
struct cmsghdr *cmsg;
const unsigned int control_size = CMSG_SPACE(sizeof(int));
- char control[control_size] = { 0 };
+ char control[control_size];
uint8_t pkt_status;
+ memset(control, 0, sizeof(control));
recv_msbc_bytes:
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
- iov.iov_base = info->hci_sco_buf;
- iov.iov_len = HCI_SCO_PKT_SIZE;
+ iov.iov_base = info->read_buf + info->read_wp;
+ iov.iov_len = info->packet_size;
msg.msg_control = control;
msg.msg_controllen = control_size;
@@ -433,11 +481,21 @@
* Treat return code 0 (socket shutdown) as error here. BT stack
* shall send signal to main thread for device disconnection.
*/
- if (err != HCI_SCO_PKT_SIZE) {
+ if (err != (int)info->packet_size) {
syslog(LOG_ERR, "Partially read %d bytes for mSBC packet", err);
return -1;
}
+ /* Offset in input data breaks mSBC frame parsing. Discard this packet
+ * until read alignment succeed. */
+ if (info->read_align_cb) {
+ if (!info->read_align_cb(info->read_buf))
+ return 0;
+ else
+ info->read_align_cb = NULL;
+ }
+ info->read_wp += err;
+
pkt_status = 0;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
@@ -454,26 +512,47 @@
* 0x01 - possibly invalid data.
* 0x10 - No data received.
* 0x11 - Data partially lost.
+ *
+ * If the latest SCO packet read doesn't cross the boundary of a mSBC
+ * frame, the packet status flag can be used to derive if the current
+ * mSBC frame is corrupted.
*/
- if (pkt_status) {
- syslog(LOG_ERR, "HCI SCO status flag %u", pkt_status);
- return handle_packet_loss(info);
+ if (info->read_rp + MSBC_PKT_SIZE >= info->read_wp)
+ info->msbc_read_current_corrupted |= (pkt_status > 0);
+
+ /* Read buffer not enough to parse another mSBC frame. */
+ if (info->read_rp + MSBC_PKT_SIZE > info->read_wp)
+ return 0;
+
+ if (info->msbc_read_current_corrupted) {
+ syslog(LOG_DEBUG, "mSBC frame corrputed from packet status");
+ info->msbc_read_current_corrupted = 0;
+ frame_head = NULL;
+ } else {
+ frame_head =
+ extract_msbc_frame(info->read_buf + info->read_rp,
+ info->read_wp - info->read_rp, &seq);
+ if (!frame_head)
+ syslog(LOG_DEBUG, "Failed to extract msbc frame");
}
- /* There is chance that erroneous data reporting gives us false positive.
- * If mSBC frame extraction fails, we shall handle it as packet loss.
+ /*
+ * Done with parsing the raw bytes just read. If mSBC frame head not
+ * found, we shall handle it as packet loss.
*/
- frame_head = extract_msbc_frame(info->hci_sco_buf, MSBC_PKT_SIZE, &seq);
- if (!frame_head) {
- syslog(LOG_ERR, "Failed to extract msbc frame");
- return handle_packet_loss(info);
+ info->read_rp += MSBC_PKT_SIZE;
+ if (info->read_rp == info->read_wp) {
+ info->read_rp = 0;
+ info->read_wp = 0;
}
+ if (!frame_head)
+ return handle_packet_loss(info);
/*
* Consider packet loss when found discontinuity in sequence number.
*/
while (seq != (info->msbc_num_in_frames % 4)) {
- syslog(LOG_ERR, "SCO packet seq unmatch");
+ syslog(LOG_DEBUG, "SCO packet seq unmatch");
err = handle_packet_loss(info);
if (err < 0)
return err;
@@ -660,6 +739,22 @@
buf_reset(info->capture_buf);
if (codec == HFP_CODEC_ID_MSBC) {
+ int i;
+ for (i = 0; wbs_supported_packet_size[i] != 0; i++) {
+ if (info->packet_size == wbs_supported_packet_size[i])
+ break;
+ }
+ /* In case of unsupported value, error log and fallback to
+ * MSBC_PKT_SIZE(60). */
+ if (wbs_supported_packet_size[i] == 0) {
+ syslog(LOG_ERR, "Unsupported packet size %u",
+ info->packet_size);
+ i = 0;
+ }
+ info->packet_size = wbs_supported_packet_size[i];
+ info->write_buf = (uint8_t *)malloc(wbs_hci_sco_buffer_size[i]);
+ info->read_buf = (uint8_t *)malloc(wbs_hci_sco_buffer_size[i]);
+
info->write_cb = hfp_write_msbc;
info->read_cb = hfp_read_msbc;
info->msbc_read = cras_msbc_codec_create();
@@ -677,6 +772,15 @@
info->msbc_num_out_frames = 0;
info->msbc_num_in_frames = 0;
info->msbc_num_lost_frames = 0;
+ info->write_rp = 0;
+ info->write_wp = 0;
+ info->read_rp = 0;
+ info->read_wp = 0;
+
+ /* Mark as aligned if packet size equals to MSBC_PKT_SIZE. */
+ info->read_align_cb =
+ (info->packet_size == MSBC_PKT_SIZE) ? NULL : msbc_frame_align;
+ info->msbc_read_current_corrupted = 0;
return 0;
}
@@ -697,6 +801,11 @@
info->write_cb = NULL;
info->read_cb = NULL;
+ if (info->write_buf)
+ free(info->write_buf);
+ if (info->read_buf)
+ free(info->read_buf);
+
if (info->msbc_read) {
cras_sbc_codec_destroy(info->msbc_read);
info->msbc_read = NULL;
diff --git a/cras/src/server/cras_hfp_slc.c b/cras/src/server/cras_hfp_slc.c
index ff56d57..1cf003a 100644
--- a/cras/src/server/cras_hfp_slc.c
+++ b/cras/src/server/cras_hfp_slc.c
@@ -836,6 +836,7 @@
gain);
return hfp_send(handle, AT_CMD("ERROR"));
}
+ BTLOG(btlog, BT_HFP_UPDATE_SPEAKER_GAIN, gain, 0);
cras_bt_device_update_hardware_volume(handle->device,
(gain + 1) * 100 / 16);
}
@@ -903,6 +904,7 @@
/* Normailize gain value to 0-15 */
gain = gain * 15 / 100;
+ BTLOG(btlog, BT_HFP_SET_SPEAKER_GAIN, gain, 0);
snprintf(command, 128, AT_CMD("+VGS=%d"), gain);
return hfp_send(handle, command);
@@ -1297,3 +1299,8 @@
{
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 90c1e79..c3cdc11 100644
--- a/cras/src/server/cras_hfp_slc.h
+++ b/cras/src/server/cras_hfp_slc.h
@@ -145,6 +145,10 @@
* 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 60dc34c..f426eb5 100644
--- a/cras/src/server/cras_iodev.c
+++ b/cras/src/server/cras_iodev.c
@@ -21,6 +21,7 @@
#include "cras_dsp_pipeline.h"
#include "cras_fmt_conv.h"
#include "cras_iodev.h"
+#include "cras_main_thread_log.h"
#include "cras_iodev_list.h"
#include "cras_mix.h"
#include "cras_ramp.h"
@@ -685,6 +686,8 @@
if (node->plugged == plugged)
return;
node->plugged = plugged;
+ MAINLOG(main_log, MAIN_THREAD_NODE_PLUGGED, node->dev->info.idx,
+ plugged, 0);
if (plugged) {
gettimeofday(&node->plugged_time, NULL);
} else if (node == node->dev->active_node) {
@@ -1016,7 +1019,8 @@
rc = iodev->close_dev(iodev);
if (rc)
- return rc;
+ syslog(LOG_ERR, "Error closing dev %s, rc %d", iodev->info.name,
+ rc);
iodev->state = CRAS_IODEV_STATE_CLOSE;
if (iodev->ramp)
cras_ramp_reset(iodev->ramp);
@@ -1271,9 +1275,6 @@
int rc;
rc = iodev->frames_queued(iodev, hw_tstamp);
- if (rc == -EPIPE)
- cras_audio_thread_event_severe_underrun();
-
if (rc < 0)
return rc;
@@ -1323,7 +1324,7 @@
/* This assumes consecutive channel areas. */
buf = area->channels[0].buf;
- memset(buf, 0, frames_written * frame_bytes);
+ memset(buf, 0, (size_t)frames_written * (size_t)frame_bytes);
cras_iodev_put_output_buffer(odev, buf, frames_written, NULL,
NULL);
frames -= frames_written;
diff --git a/cras/src/server/cras_iodev.h b/cras/src/server/cras_iodev.h
index bdaa36f..553cb80 100644
--- a/cras/src/server/cras_iodev.h
+++ b/cras/src/server/cras_iodev.h
@@ -103,9 +103,6 @@
* left_right_swapped - If left and right output channels are swapped.
* type - Type displayed to the user.
* position - Specify where on the system this node locates.
- * mic_positions - Whitespace-separated microphone positions using Cartesian
- * coordinates in meters with ordering x, y, z. The string is formatted as:
- * "x1 y1 z1 ... xn yn zn" for an n-microphone array.
* name - Name displayed to the user.
* dsp_name - The "DspName" variable specified in the ucm config.
* active_hotword_model - name of the currently selected hotword model.
@@ -129,7 +126,6 @@
int left_right_swapped;
enum CRAS_NODE_TYPE type;
enum CRAS_NODE_POSITION position;
- char mic_positions[CRAS_NODE_MIC_POS_BUFFER_SIZE];
char name[CRAS_NODE_NAME_BUFFER_SIZE];
const char *dsp_name;
char active_hotword_model[CRAS_NODE_HOTWORD_MODEL_BUFFER_SIZE];
diff --git a/cras/src/server/cras_iodev_list.c b/cras/src/server/cras_iodev_list.c
index d6a92cb..bba793d 100644
--- a/cras/src/server/cras_iodev_list.c
+++ b/cras/src/server/cras_iodev_list.c
@@ -11,6 +11,7 @@
#include "cras_iodev_info.h"
#include "cras_iodev_list.h"
#include "cras_loopback_iodev.h"
+#include "cras_main_thread_log.h"
#include "cras_observer.h"
#include "cras_rstream.h"
#include "cras_server.h"
@@ -53,6 +54,8 @@
struct device_enabled_cb *next, *prev;
};
+struct main_thread_event_log *main_log;
+
/* Lists for devs[CRAS_STREAM_INPUT] and devs[CRAS_STREAM_OUTPUT]. */
static struct iodev_list devs[CRAS_NUM_DIRECTIONS];
/* The observer client iodev_list used to listen on various events. */
@@ -261,7 +264,6 @@
node_info->left_right_swapped =
node->left_right_swapped;
node_info->stable_id = node->stable_id;
- strcpy(node_info->mic_positions, node->mic_positions);
strcpy(node_info->name, node->name);
strcpy(node_info->active_hotword_model,
node->active_hotword_model);
@@ -375,33 +377,18 @@
}
/*
- * Close dev if it's opened, without the extra call to idle_dev_check.
- * This is useful for closing a dev inside idle_dev_check function to
- * avoid infinite recursive call.
- *
- * Returns:
- * -EINVAL if device was not opened, otherwise return 0.
+ * Removes all attached streams and close dev if it's opened.
*/
-static int close_dev_without_idle_check(struct cras_iodev *dev)
+static void close_dev(struct cras_iodev *dev)
{
if (!cras_iodev_is_open(dev))
- return -EINVAL;
+ return;
+ 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);
possibly_disable_echo_reference(dev);
- return 0;
-}
-
-static void close_dev(struct cras_iodev *dev)
-{
- if (close_dev_without_idle_check(dev))
- return;
-
- if (idle_timer)
- cras_tm_cancel_timer(cras_system_state_get_tm(), idle_timer);
- idle_dev_check(NULL, NULL);
}
static void idle_dev_check(struct cras_timer *timer, void *data)
@@ -420,7 +407,7 @@
if (edev->dev->idle_timeout.tv_sec == 0)
continue;
if (timespec_after(&now, &edev->dev->idle_timeout)) {
- close_dev_without_idle_check(edev->dev);
+ close_dev(edev->dev);
continue;
}
num_idle_devs++;
@@ -475,6 +462,8 @@
if (cras_iodev_is_open(dev))
return 0;
cancel_pending_init_retries(dev->info.idx);
+ MAINLOG(main_log, MAIN_THREAD_DEV_INIT, dev->info.idx,
+ rstream->format.num_channels, rstream->format.frame_rate);
rc = cras_iodev_open(dev, rstream->cb_threshold, &rstream->format);
if (rc)
@@ -494,6 +483,8 @@
struct enabled_dev *edev;
struct cras_rstream *rstream;
+ MAINLOG(main_log, MAIN_THREAD_SUSPEND_DEVS, 0, 0, 0);
+
DL_FOREACH (stream_list_get(stream_list), rstream) {
if (rstream->is_pinned) {
struct cras_iodev *dev;
@@ -533,6 +524,8 @@
int has_output_stream = 0;
stream_list_suspended = 0;
+ MAINLOG(main_log, MAIN_THREAD_RESUME_DEVS, 0, 0, 0);
+
/*
* To remove the short popped noise caused by applications that can not
* stop playback "right away" after resume, we mute all output devices
@@ -683,6 +676,11 @@
if (!can_attach)
continue;
+ /*
+ * Note that the stream list is descending ordered by channel
+ * count, which guarantees the first attachable stream will have
+ * the highest channel count.
+ */
rc = init_device(dev, stream);
if (rc) {
syslog(LOG_ERR, "Enable %s failed, rc = %d",
@@ -754,6 +752,11 @@
return 0;
}
+/*
+ * Close device enabled by pinned stream. Since it's NOT in the enabled
+ * dev list, make sure update_active_node() is called to correctly
+ * configure the ALSA UCM or BT profile state.
+ */
static int close_pinned_device(struct cras_iodev *dev)
{
close_dev(dev);
@@ -807,34 +810,64 @@
struct cras_iodev *iodevs[10];
unsigned int num_iodevs;
int rc;
+ bool iodev_reopened;
if (stream_list_suspended)
return 0;
+ MAINLOG(main_log, MAIN_THREAD_STREAM_ADDED, rstream->stream_id,
+ rstream->direction, rstream->buffer_frames);
+
if (rstream->is_pinned)
return pinned_stream_added(rstream);
/* Add the new stream to all enabled iodevs at once to avoid offset
* in shm level between different ouput iodevs. */
num_iodevs = 0;
+ iodev_reopened = false;
DL_FOREACH (enabled_devs[rstream->direction], edev) {
if (num_iodevs >= ARRAY_SIZE(iodevs)) {
syslog(LOG_ERR, "too many enabled devices");
break;
}
- rc = init_device(edev->dev, rstream);
- if (rc) {
- /* Error log but don't return error here, because
- * stopping audio could block video playback.
+ if (cras_iodev_is_open(edev->dev) &&
+ (rstream->format.num_channels >
+ edev->dev->format->num_channels) &&
+ (rstream->format.num_channels <=
+ edev->dev->info.max_supported_channels)) {
+ /* Re-open the device with the format of the attached
+ * stream if it has higher channel count than the
+ * current format of the device, and doesn't exceed the
+ * max_supported_channels of the device.
+ * Fallback device will be transciently enabled during
+ * the device re-opening.
*/
- syslog(LOG_ERR, "Init %s failed, rc = %d",
- edev->dev->info.name, rc);
- schedule_init_device_retry(edev->dev);
- continue;
- }
+ MAINLOG(main_log, MAIN_THREAD_DEV_REOPEN,
+ rstream->format.num_channels,
+ edev->dev->format->num_channels,
+ edev->dev->format->frame_rate);
+ syslog(LOG_INFO, "re-open %s for higher channel count",
+ edev->dev->info.name);
+ possibly_enable_fallback(rstream->direction, false);
+ cras_iodev_list_suspend_dev(edev->dev->info.idx);
+ cras_iodev_list_resume_dev(edev->dev->info.idx);
+ possibly_disable_fallback(rstream->direction);
+ iodev_reopened = true;
+ } else {
+ rc = init_device(edev->dev, rstream);
+ if (rc) {
+ /* Error log but don't return error here, because
+ * stopping audio could block video playback.
+ */
+ syslog(LOG_ERR, "Init %s failed, rc = %d",
+ edev->dev->info.name, rc);
+ schedule_init_device_retry(edev->dev);
+ continue;
+ }
- iodevs[num_iodevs++] = edev->dev;
+ iodevs[num_iodevs++] = edev->dev;
+ }
}
if (num_iodevs) {
rc = add_stream_to_open_devs(rstream, iodevs, num_iodevs);
@@ -842,9 +875,9 @@
syslog(LOG_ERR, "adding stream to thread fail");
return rc;
}
- } else {
+ } else if (!iodev_reopened) {
/* Enable fallback device if no other iodevs can be initialized
- * successfully.
+ * or re-opened successfully.
* For error codes like EAGAIN and ENOENT, a new iodev will be
* enabled soon so streams are going to route there. As for the
* rest of the error cases, silence will be played or recorded
@@ -911,6 +944,8 @@
if (rc)
return rc;
+ MAINLOG(main_log, MAIN_THREAD_STREAM_REMOVED, rstream->stream_id, 0, 0);
+
if (rstream->is_pinned)
pinned_stream_removed(rstream);
@@ -957,6 +992,7 @@
struct cras_rstream *stream;
struct device_enabled_cb *callback;
+ MAINLOG(main_log, MAIN_THREAD_DEV_DISABLE, dev->info.idx, force, 0);
/*
* Remove from enabled dev list. However this dev could have a stream
* pinned to it, only cancel pending init timers when force flag is set.
@@ -964,26 +1000,23 @@
DL_DELETE(enabled_devs[dir], edev);
free(edev);
dev->is_enabled = 0;
- if (force)
+ if (force) {
cancel_pending_init_retries(dev->info.idx);
-
- /*
- * Pull all default streams off this device.
- * Pull all pinned streams off as well if force is true.
- */
- DL_FOREACH (stream_list_get(stream_list), stream) {
- if (stream->direction != dev->direction)
- continue;
- if (stream->is_pinned && !force)
- continue;
- audio_thread_disconnect_stream(audio_thread, stream, dev);
}
- /* If this is a force disable call, that guarantees pinned streams have
- * all been detached. Otherwise check with stream_list to see if
- * there's still a pinned stream using this device.
- */
- if (!force && stream_list_has_pinned_stream(stream_list, dev->info.idx))
+ /* If there's a pinned stream exists, simply disconnect all the normal
+ * streams off this device and return. */
+ else if (stream_list_has_pinned_stream(stream_list, dev->info.idx)) {
+ DL_FOREACH (stream_list_get(stream_list), stream) {
+ if (stream->direction != dev->direction)
+ continue;
+ if (stream->is_pinned)
+ continue;
+ audio_thread_disconnect_stream(audio_thread, stream,
+ dev);
+ }
return 0;
+ }
+
DL_FOREACH (device_enable_cbs, callback)
callback->disabled_cb(dev, callback->cb_data);
close_dev(dev);
@@ -993,36 +1026,6 @@
}
/*
- * Assume the device is not in enabled_devs list.
- * Assume there is no default stream on the device.
- * An example is that this device is unplugged while it is playing
- * a pinned stream. The device and stream may have been removed in
- * audio thread due to I/O error handling.
- */
-static int force_close_pinned_only_device(struct cras_iodev *dev)
-{
- struct cras_rstream *rstream;
-
- /* Pull pinned streams off this device. Note that this is initiated
- * from server side, so the pin stream still exist in stream_list
- * pending client side to actually remove it.
- */
- DL_FOREACH (stream_list_get(stream_list), rstream) {
- if (rstream->direction != dev->direction)
- continue;
- if (!rstream->is_pinned)
- continue;
- if (dev->info.idx != rstream->pinned_dev_idx)
- continue;
- audio_thread_disconnect_stream(audio_thread, rstream, dev);
- }
-
- close_dev(dev);
- dev->update_active_node(dev, dev->active_node->idx, 0);
- return 0;
-}
-
-/*
* Exported Interface.
*/
@@ -1038,6 +1041,8 @@
list_observer = cras_observer_add(&observer_ops, NULL);
idle_timer = NULL;
+ main_log = main_thread_event_log_init();
+
/* Create the audio stream list for the system. */
stream_list =
stream_list_create(stream_added_cb, stream_removed_cb,
@@ -1079,6 +1084,7 @@
empty_iodev_destroy(fallback_devs[CRAS_STREAM_INPUT]);
empty_iodev_destroy(fallback_devs[CRAS_STREAM_OUTPUT]);
stream_list_destroy(stream_list);
+ main_thread_event_log_deinit(main_log);
if (list_observer) {
cras_observer_remove(list_observer);
list_observer = NULL;
@@ -1114,6 +1120,8 @@
if (!new_dev || new_dev->direction != dir)
return;
+ MAINLOG(main_log, MAIN_THREAD_ADD_ACTIVE_NODE, new_dev->info.idx, 0, 0);
+
/* If the new dev is already enabled but its active node needs to be
* changed. Disable new dev first, update active node, and then
* re-enable it again.
@@ -1153,7 +1161,7 @@
*/
if (!edev_to_disable) {
if (force_close)
- force_close_pinned_only_device(dev);
+ close_pinned_device(dev);
return;
}
@@ -1171,25 +1179,13 @@
void cras_iodev_list_suspend_dev(unsigned int dev_idx)
{
- struct cras_rstream *rstream;
struct cras_iodev *dev = find_dev(dev_idx);
if (!dev)
return;
- DL_FOREACH (stream_list_get(stream_list), rstream) {
- if (rstream->direction != dev->direction)
- continue;
- /* Disconnect all streams that are either:
- * (1) normal stream while dev is enabled by UI, or
- * (2) stream specifically pins to this dev.
- */
- if ((dev->is_enabled && !rstream->is_pinned) ||
- (rstream->is_pinned &&
- (dev->info.idx != rstream->pinned_dev_idx)))
- audio_thread_disconnect_stream(audio_thread, rstream,
- dev);
- }
+ /* Remove all streams including the pinned streams, and close
+ * this iodev. */
close_dev(dev);
dev->update_active_node(dev, dev->active_node->idx, 0);
}
@@ -1250,6 +1246,8 @@
if (rc)
return rc;
+ MAINLOG(main_log, MAIN_THREAD_ADD_TO_DEV_LIST, output->info.idx,
+ CRAS_STREAM_OUTPUT, 0);
return 0;
}
@@ -1264,6 +1262,8 @@
if (rc)
return rc;
+ MAINLOG(main_log, MAIN_THREAD_ADD_TO_DEV_LIST, input->info.idx,
+ CRAS_STREAM_INPUT, 0);
return 0;
}
@@ -1513,6 +1513,8 @@
/* find the devices for the id. */
new_dev = find_dev(dev_index_of(node_id));
+ MAINLOG(main_log, MAIN_THREAD_SELECT_NODE, dev_index_of(node_id), 0, 0);
+
/* Do nothing if the direction is mismatched. The new_dev == NULL case
could happen if node_id is 0 (no selection), or the client tries
to select a non-existing node (maybe it's unplugged just before
@@ -1619,6 +1621,8 @@
if (iodev->set_volume)
iodev->set_volume(iodev);
cras_iodev_list_notify_node_volume(node);
+ MAINLOG(main_log, MAIN_THREAD_OUTPUT_NODE_VOLUME, iodev->info.idx,
+ volume, 0);
return 0;
}
@@ -1626,6 +1630,7 @@
unsigned int node_idx, int value)
{
struct cras_ionode *node;
+ int db_scale;
node = find_node(iodev, node_idx);
if (!node)
@@ -1637,14 +1642,17 @@
if (value > 100)
value = 100;
- /* Linear maps (0, 100) to (-4000, 4000) dBFS. Calculate and store
- * corresponding scaler in ui_gain_scaler. */
+ /* Linear maps (0, 50) to (-4000, 0) and (50, 100) to (0, 2000) dBFS.
+ * Calculate and store corresponding scaler in ui_gain_scaler. */
+ db_scale = (value > 50) ? 40 : 80;
node->ui_gain_scaler =
- convert_softvol_scaler_from_dB((value - 50) * 80);
+ convert_softvol_scaler_from_dB((value - 50) * db_scale);
if (iodev->set_capture_gain)
iodev->set_capture_gain(iodev);
cras_iodev_list_notify_node_capture_gain(node);
+ MAINLOG(main_log, MAIN_THREAD_INPUT_NODE_GAIN, iodev->info.idx, value,
+ 0);
return 0;
}
diff --git a/cras/src/server/cras_loopback_iodev.c b/cras/src/server/cras_loopback_iodev.c
index 0faa28f..0947313 100644
--- a/cras/src/server/cras_loopback_iodev.c
+++ b/cras/src/server/cras_loopback_iodev.c
@@ -236,7 +236,7 @@
struct byte_buffer *sbuf = loopdev->sample_buffer;
unsigned int frame_bytes = cras_get_format_bytes(iodev->format);
- buf_increment_read(sbuf, nframes * frame_bytes);
+ buf_increment_read(sbuf, (size_t)nframes * (size_t)frame_bytes);
loopdev->read_frames += nframes;
ATLOG(atlog, AUDIO_THREAD_LOOPBACK_PUT, nframes, 0, 0);
return 0;
diff --git a/cras/src/server/cras_main_thread_log.h b/cras/src/server/cras_main_thread_log.h
new file mode 100644
index 0000000..1d92585
--- /dev/null
+++ b/cras/src/server/cras_main_thread_log.h
@@ -0,0 +1,64 @@
+/* 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_MAIN_THREAD_LOG_H_
+#define CRAS_MAIN_THREAD_LOG_H_
+
+#include <stdint.h>
+
+#include "cras_types.h"
+
+#define CRAS_MAIN_THREAD_LOGGING 1
+
+#if (CRAS_MAIN_THREAD_LOGGING)
+#define MAINLOG(log, event, data1, data2, data3) \
+ main_thread_event_log_data(log, event, data1, data2, data3);
+#else
+#define MAINLOG(log, event, data1, data2, data3)
+#endif
+
+extern struct main_thread_event_log *main_log;
+
+static inline struct main_thread_event_log *main_thread_event_log_init()
+{
+ struct main_thread_event_log *log;
+ log = (struct main_thread_event_log *)calloc(
+ 1, sizeof(struct main_thread_event_log));
+ if (!log)
+ return NULL;
+
+ log->len = MAIN_THREAD_EVENT_LOG_SIZE;
+ return log;
+}
+
+static inline void
+main_thread_event_log_deinit(struct main_thread_event_log *log)
+{
+ if (log)
+ free(log);
+}
+
+static inline void main_thread_event_log_data(struct main_thread_event_log *log,
+ enum MAIN_THREAD_LOG_EVENTS event,
+ uint32_t data1, uint32_t data2,
+ uint32_t data3)
+{
+ struct timespec now;
+
+ if (!log)
+ return;
+
+ clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+ log->log[log->write_pos].tag_sec =
+ (event << 24) | ((now.tv_sec % 86400) & 0x00ffffff);
+ log->log[log->write_pos].nsec = now.tv_nsec;
+ log->log[log->write_pos].data1 = data1;
+ log->log[log->write_pos].data2 = data2;
+ log->log[log->write_pos].data3 = data3;
+ log->write_pos++;
+ log->write_pos %= MAIN_THREAD_EVENT_LOG_SIZE;
+}
+
+#endif /* CRAS_MAIN_THREAD_LOG_H_ */
diff --git a/cras/src/server/cras_server_metrics.c b/cras/src/server/cras_server_metrics.c
index 1038c92..91556d8 100644
--- a/cras/src/server/cras_server_metrics.c
+++ b/cras/src/server/cras_server_metrics.c
@@ -302,6 +302,8 @@
return "CrOSVM";
case CRAS_CLIENT_TYPE_SERVER_STREAM:
return "ServerStream";
+ case CRAS_CLIENT_TYPE_LACROS:
+ return "LaCrOS";
default:
return "InvalidType";
}
@@ -971,6 +973,11 @@
{
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);
+
snprintf(metrics_name, METRICS_NAME_BUFFER_SIZE,
"Cras.%sStreamRuntime.%s",
data.direction == CRAS_STREAM_INPUT ? "Input" : "Output",
@@ -1008,10 +1015,15 @@
va_start(valist, sample);
- for (i = 0; i < num; i++) {
- len += snprintf(metrics_name + len,
+ 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);
}
diff --git a/cras/src/server/cras_system_state.c b/cras/src/server/cras_system_state.c
index 14403c8..a583419 100644
--- a/cras/src/server/cras_system_state.c
+++ b/cras/src/server/cras_system_state.c
@@ -394,6 +394,11 @@
bool cras_system_check_ignore_ucm_suffix(const char *card_name)
{
+ /* Check the general case: ALSA Loopback card "Loopback". */
+ if (!strcmp("Loopback", card_name))
+ return true;
+
+ /* Check board-specific ignore ucm suffix cards. */
struct name_list *card;
DL_FOREACH (state.ignore_suffix_cards, card) {
if (!strcmp(card->name, card_name))
diff --git a/cras/src/server/dev_io.c b/cras/src/server/dev_io.c
index 1a0b54f..20b25f7 100644
--- a/cras/src/server/dev_io.c
+++ b/cras/src/server/dev_io.c
@@ -92,7 +92,7 @@
return count;
}
-static void check_non_empty_state_transition(struct open_dev *adevs)
+int dev_io_check_non_empty_state_transition(struct open_dev *adevs)
{
int new_non_empty_dev_count = count_non_empty_dev(adevs);
@@ -103,6 +103,7 @@
0);
non_empty_device_count = new_non_empty_dev_count;
+ return non_empty_device_count > 0;
}
/* Checks whether it is time to fetch. */
@@ -385,11 +386,14 @@
clock_gettime(CLOCK_MONOTONIC_RAW, &level_tstamp);
/*
- * If any input device has more than largest_cb_level * 1.5 frames, need to
- * drop frames from all devices.
+ * Drop frames from all devices if any device meets these requirements:
+ * 1. The hw_level is larger than largest_cb_level * 1.5 or larger than
+ * buffer_size * 0.5.
+ * 2. The time of those frames is larger than DROP_FRAMES_THRESHOLD_MS.
*/
if (input_devices_can_drop_samples(adev->dev) &&
- rc >= adev->dev->largest_cb_level * 1.5 &&
+ (rc >= adev->dev->largest_cb_level * 1.5 ||
+ rc >= adev->dev->buffer_size * 0.5) &&
cras_frames_to_ms(rc, adev->dev->format->frame_rate) >=
DROP_FRAMES_THRESHOLD_MS)
*need_to_drop = true;
@@ -849,7 +853,7 @@
static void dev_io_drop_samples(struct open_dev *idev_list)
{
struct open_dev *adev;
- struct timespec drop_time;
+ struct timespec drop_time = {};
int rc;
get_input_devices_drop_time(idev_list, &drop_time);
@@ -919,6 +923,7 @@
ATLOG(atlog, AUDIO_THREAD_SEVERE_UNDERRUN, adev->dev->info.idx,
0, 0);
cras_iodev_reset_request(adev->dev);
+ cras_audio_thread_event_severe_underrun();
}
/* Device error, remove it. */
dev_io_rm_open_dev(odevs, adev);
@@ -1087,8 +1092,6 @@
dev_io_capture(idevs);
dev_io_send_captured_samples(*idevs);
dev_io_playback_write(odevs, output_converter);
-
- check_non_empty_state_transition(*odevs);
}
static int input_adev_ignore_wake(const struct open_dev *adev)
@@ -1214,7 +1217,7 @@
cras_server_metrics_highest_hw_level(dev_to_rm->dev->highest_hw_level,
dev_to_rm->dev->direction);
- check_non_empty_state_transition(*odev_list);
+ dev_io_check_non_empty_state_transition(*odev_list);
ATLOG(atlog, AUDIO_THREAD_DEV_REMOVED, dev_to_rm->dev->info.idx, 0, 0);
diff --git a/cras/src/server/dev_io.h b/cras/src/server/dev_io.h
index 51aa319..259bbab 100644
--- a/cras/src/server/dev_io.h
+++ b/cras/src/server/dev_io.h
@@ -71,6 +71,12 @@
struct cras_fmt_conv *output_converter);
/*
+ * Checks the non-empty device state in active output lists and return
+ * if there's at least one non-empty device.
+ */
+int dev_io_check_non_empty_state_transition(struct open_dev *adevs);
+
+/*
* Fills min_ts with the next time the system should wake to service input.
* Returns the number of devices waiting.
*/
diff --git a/cras/src/server/dev_stream.c b/cras/src/server/dev_stream.c
index 02da56a..f0fcb71 100644
--- a/cras/src/server/dev_stream.c
+++ b/cras/src/server/dev_stream.c
@@ -259,7 +259,8 @@
total_read += read_frames;
source_samples += read_frames * source_frame_bytes;
buf_increment_write(dev_stream->conv_buffer,
- write_frames * dst_frame_bytes);
+ (size_t)write_frames *
+ (size_t)dst_frame_bytes);
}
return total_read;
@@ -317,7 +318,7 @@
software_gain_scaler);
buf_increment_read(dev_stream->conv_buffer,
- write_frames * frame_bytes);
+ (size_t)write_frames * (size_t)frame_bytes);
total_written += write_frames;
cras_rstream_dev_offset_update(rstream, write_frames,
dev_stream->dev_id);
diff --git a/cras/src/server/float_buffer.h b/cras/src/server/float_buffer.h
index 39a0187..ba3523d 100644
--- a/cras/src/server/float_buffer.h
+++ b/cras/src/server/float_buffer.h
@@ -37,7 +37,7 @@
b->fp = (float **)malloc(num_channels * sizeof(float *));
b->buf = (struct byte_buffer *)calloc(
1, sizeof(struct byte_buffer) +
- max_size * num_channels * sizeof(float));
+ sizeof(float) * max_size * num_channels);
b->buf->max_size = max_size;
b->buf->used_size = max_size;
return b;
diff --git a/cras/src/server/rust/src/headers/rate_estimator.h b/cras/src/server/rust/src/headers/rate_estimator.h
index f8726ba..3ac9cfa 100644
--- a/cras/src/server/rust/src/headers/rate_estimator.h
+++ b/cras/src/server/rust/src/headers/rate_estimator.h
@@ -31,12 +31,16 @@
typedef struct rate_estimator rate_estimator;
/**
+ * # Safety
+ *
* To use this function safely, `re` must be a pointer returned from
* rate_estimator_create, or null.
*/
void rate_estimator_add_frames(rate_estimator *re, int frames);
/**
+ * # Safety
+ *
* To use this function safely, `re` must be a pointer returned from
* rate_estimator_create, or null, and `now` must be a valid pointer to a
* timespec.
@@ -45,6 +49,8 @@
const struct timespec *now);
/**
+ * # Safety
+ *
* To use this function safely, `window_size` must be a valid pointer to a
* timespec.
*/
@@ -53,18 +59,24 @@
double smooth_factor);
/**
+ * # Safety
+ *
* To use this function safely, `re` must be a pointer returned from
* rate_estimator_create, or null.
*/
void rate_estimator_destroy(rate_estimator *re);
/**
+ * # Safety
+ *
* To use this function safely, `re` must be a pointer returned from
* rate_estimator_create, or null.
*/
double rate_estimator_get_rate(const rate_estimator *re);
/**
+ * # Safety
+ *
* To use this function safely, `re` must be a pointer returned from
* rate_estimator_create, or null.
*/
diff --git a/cras/src/server/rust/src/rate_estimator.rs b/cras/src/server/rust/src/rate_estimator.rs
index d41c725..585f346 100644
--- a/cras/src/server/rust/src/rate_estimator.rs
+++ b/cras/src/server/rust/src/rate_estimator.rs
@@ -183,6 +183,6 @@
self.window_frames = 0;
return true;
}
- return false;
+ false
}
}
diff --git a/cras/src/server/rust/src/rate_estimator_bindings.rs b/cras/src/server/rust/src/rate_estimator_bindings.rs
index 129c63d..3073ba7 100644
--- a/cras/src/server/rust/src/rate_estimator_bindings.rs
+++ b/cras/src/server/rust/src/rate_estimator_bindings.rs
@@ -8,9 +8,11 @@
use crate::RateEstimator;
-#[no_mangle]
+/// # Safety
+///
/// To use this function safely, `window_size` must be a valid pointer to a
/// timespec.
+#[no_mangle]
pub unsafe extern "C" fn rate_estimator_create(
rate: libc::c_uint,
window_size: *const libc::timespec,
@@ -29,9 +31,11 @@
}
}
-#[no_mangle]
+/// # Safety
+///
/// To use this function safely, `re` must be a pointer returned from
/// rate_estimator_create, or null.
+#[no_mangle]
pub unsafe extern "C" fn rate_estimator_destroy(re: *mut RateEstimator) {
if re.is_null() {
return;
@@ -40,9 +44,11 @@
drop(Box::from_raw(re));
}
-#[no_mangle]
+/// # Safety
+///
/// To use this function safely, `re` must be a pointer returned from
/// rate_estimator_create, or null.
+#[no_mangle]
pub unsafe extern "C" fn rate_estimator_add_frames(re: *mut RateEstimator, frames: libc::c_int) {
if re.is_null() {
return;
@@ -51,10 +57,12 @@
(*re).add_frames(frames)
}
-#[no_mangle]
+/// # Safety
+///
/// To use this function safely, `re` must be a pointer returned from
/// rate_estimator_create, or null, and `now` must be a valid pointer to a
/// timespec.
+#[no_mangle]
pub unsafe extern "C" fn rate_estimator_check(
re: *mut RateEstimator,
level: libc::c_int,
@@ -75,9 +83,11 @@
(*re).update_estimated_rate(level, now) as i32
}
-#[no_mangle]
+/// # Safety
+///
/// To use this function safely, `re` must be a pointer returned from
/// rate_estimator_create, or null.
+#[no_mangle]
pub unsafe extern "C" fn rate_estimator_get_rate(re: *const RateEstimator) -> libc::c_double {
if re.is_null() {
return 0.0;
@@ -86,9 +96,11 @@
(*re).get_estimated_rate()
}
-#[no_mangle]
+/// # Safety
+///
/// To use this function safely, `re` must be a pointer returned from
/// rate_estimator_create, or null.
+#[no_mangle]
pub unsafe extern "C" fn rate_estimator_reset_rate(re: *mut RateEstimator, rate: libc::c_uint) {
if re.is_null() {
return;
diff --git a/cras/src/server/stream_list.c b/cras/src/server/stream_list.c
index d247ec8..719608a 100644
--- a/cras/src/server/stream_list.c
+++ b/cras/src/server/stream_list.c
@@ -81,12 +81,19 @@
struct cras_rstream **stream)
{
int rc;
+ struct cras_rstream *next_stream;
rc = list->stream_create_cb(stream_config, stream);
if (rc)
return rc;
- DL_APPEND(list->streams, *stream);
+ /* Keep stream list in descending order by channel count. */
+ DL_FOREACH (list->streams, next_stream) {
+ if ((*stream)->format.num_channels >=
+ next_stream->format.num_channels)
+ break;
+ }
+ DL_INSERT(list->streams, next_stream, *stream);
rc = list->stream_added_cb(*stream);
if (rc) {
DL_DELETE(list->streams, *stream);
diff --git a/cras/src/server/stream_list.h b/cras/src/server/stream_list.h
index ae77a33..0a9b86a 100644
--- a/cras/src/server/stream_list.h
+++ b/cras/src/server/stream_list.h
@@ -30,8 +30,8 @@
struct cras_rstream *stream_list_get(struct stream_list *list);
-/* Creates a cras_rstream from cras_rstreaem_config and adds the cras_rstream
- * to stream_list.
+/* Creates a cras_rstream from cras_rstream_config and inserts the cras_rstream
+ * to stream_list in descending order by channel count.
*
* Args:
* list - stream_list to add streams.
diff --git a/cras/src/server/test_iodev.c b/cras/src/server/test_iodev.c
index e0cbccd..266b62a 100644
--- a/cras/src/server/test_iodev.c
+++ b/cras/src/server/test_iodev.c
@@ -102,7 +102,8 @@
struct test_iodev *testio = (struct test_iodev *)iodev;
/* Input */
- buf_increment_read(testio->audbuff, frames * testio->fmt_bytes);
+ buf_increment_read(testio->audbuff,
+ (size_t)frames * (size_t)testio->fmt_bytes);
return 0;
}
@@ -241,8 +242,9 @@
write_ptr = buf_write_pointer_size(testio->audbuff, &avail);
count = MIN(count, avail);
- memcpy(write_ptr, samples, count * testio->fmt_bytes);
- buf_increment_write(testio->audbuff, count * testio->fmt_bytes);
+ memcpy(write_ptr, samples, (size_t)count * (size_t)testio->fmt_bytes);
+ buf_increment_write(testio->audbuff,
+ (size_t)count * (size_t)testio->fmt_bytes);
return count;
}
diff --git a/cras/src/tests/a2dp_iodev_unittest.cc b/cras/src/tests/a2dp_iodev_unittest.cc
index 8f37de2..f03a614 100644
--- a/cras/src/tests/a2dp_iodev_unittest.cc
+++ b/cras/src/tests/a2dp_iodev_unittest.cc
@@ -722,6 +722,7 @@
int cras_bt_transport_configuration(const struct cras_bt_transport* transport,
void* configuration,
int len) {
+ memset(configuration, 0, len);
cras_bt_transport_configuration_called++;
return 0;
}
diff --git a/cras/src/tests/alsa_io_unittest.cc b/cras/src/tests/alsa_io_unittest.cc
index 56073e8..ba650a2 100644
--- a/cras/src/tests/alsa_io_unittest.cc
+++ b/cras/src/tests/alsa_io_unittest.cc
@@ -146,6 +146,7 @@
static void* audio_thread_cb_data;
static int hotword_send_triggered_msg_called;
static struct timespec clock_gettime_retspec;
+static unsigned cras_iodev_reset_rate_estimator_called;
void ResetStubData() {
cras_alsa_open_called = 0;
@@ -224,6 +225,7 @@
cras_iodev_dsp_set_swap_mode_for_node_called = 0;
ucm_get_default_node_gain_values.clear();
ucm_get_intrinsic_sensitivity_values.clear();
+ cras_iodev_reset_rate_estimator_called = 0;
}
static long fake_get_dBFS(const struct cras_volume_curve* curve,
@@ -1073,7 +1075,6 @@
}
TEST(AlsaOutputNode, MaxSupportedChannels) {
- struct alsa_io* aio;
struct cras_use_case_mgr* const fake_ucm = (struct cras_use_case_mgr*)3;
struct cras_iodev* iodev;
struct ucm_section* section;
@@ -1090,7 +1091,6 @@
1, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm,
CRAS_STREAM_OUTPUT);
ASSERT_NE(iodev, (void*)NULL);
- aio = reinterpret_cast<struct alsa_io*>(iodev);
// Node without controls or jacks.
section = ucm_section_create(INTERNAL_SPEAKER, "hw:0,1", 1, -1,
@@ -2209,6 +2209,7 @@
EXPECT_EQ(0, cras_iodev_fill_odev_zeros_frames);
EXPECT_EQ(0, aio.free_running);
EXPECT_EQ(0, aio.filled_zeros_for_draining);
+ EXPECT_EQ(1, cras_iodev_reset_rate_estimator_called);
}
TEST_F(AlsaFreeRunTestSuite, LeaveFreeRunNotInFreeRunLessRemain) {
@@ -2233,6 +2234,7 @@
EXPECT_EQ(96, cras_iodev_fill_odev_zeros_frames);
EXPECT_EQ(0, aio.free_running);
EXPECT_EQ(0, aio.filled_zeros_for_draining);
+ EXPECT_EQ(1, cras_iodev_reset_rate_estimator_called);
}
TEST_F(AlsaFreeRunTestSuite, LeaveFreeRunInFreeRun) {
@@ -2250,6 +2252,7 @@
cras_alsa_resume_appl_ptr_ahead);
EXPECT_EQ(0, aio.free_running);
EXPECT_EQ(0, aio.filled_zeros_for_draining);
+ EXPECT_EQ(1, cras_iodev_reset_rate_estimator_called);
}
// Reuse AlsaFreeRunTestSuite for output underrun handling because they are
@@ -2918,6 +2921,7 @@
void cras_iodev_free_audio_area(struct cras_iodev* iodev) {}
int cras_iodev_reset_rate_estimator(const struct cras_iodev* iodev) {
+ cras_iodev_reset_rate_estimator_called++;
return 0;
}
diff --git a/cras/src/tests/alsa_jack_unittest.cc b/cras/src/tests/alsa_jack_unittest.cc
index 47933d7..1aa9549 100644
--- a/cras/src/tests/alsa_jack_unittest.cc
+++ b/cras/src/tests/alsa_jack_unittest.cc
@@ -216,14 +216,15 @@
EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
EXPECT_EQ(ucm ? njacks : 0, ucm_get_dev_for_jack_called);
EXPECT_EQ(ucm ? njacks : 0, ucm_get_override_type_name_called);
- EXPECT_EQ(1 + nhdmi_jacks, snd_hctl_first_elem_called);
+ EXPECT_EQ(1, snd_hctl_first_elem_called);
EXPECT_EQ(njacks, snd_hctl_elem_set_callback_called);
+ EXPECT_EQ(nhdmi_jacks, snd_hctl_find_elem_called);
/* For some functions, the number of calls to them could
* be larger then expected count if there is ELD control
* in given elements. */
- EXPECT_GE(snd_hctl_elem_next_called, nelems + nhdmi_jacks);
- EXPECT_GE(snd_hctl_elem_get_name_called, nelems + njacks);
+ EXPECT_GE(snd_hctl_elem_next_called, nelems);
+ EXPECT_GE(snd_hctl_elem_get_name_called, nelems);
if (direction == CRAS_STREAM_OUTPUT) {
EXPECT_EQ(njacks, cras_alsa_mixer_get_output_matching_name_called);
@@ -678,7 +679,7 @@
ASSERT_NE(static_cast<struct cras_alsa_jack_list*>(NULL), jack_list);
/* Assert get device is called for the ELD control */
- EXPECT_EQ(1, snd_hctl_elem_get_device_called);
+ EXPECT_EQ(1, snd_hctl_find_elem_called);
cras_alsa_jack_list_destroy(jack_list);
}
diff --git a/cras/src/tests/alsa_ucm_unittest.cc b/cras/src/tests/alsa_ucm_unittest.cc
index c9baecc..efb8265 100644
--- a/cras/src/tests/alsa_ucm_unittest.cc
+++ b/cras/src/tests/alsa_ucm_unittest.cc
@@ -1140,6 +1140,7 @@
struct ucm_section* section;
struct mixer_name* m_name;
int section_count = 0;
+ int dev_idx;
int i = 0;
const char* devices[] = {"Headphone", "The headphones jack.",
"Speaker", "The speakers.",
@@ -1153,6 +1154,7 @@
"=PlaybackPCM/Speaker/HiFi",
"=CoupledMixers/Speaker/HiFi",
+ "=DependentPCM/Speaker/HiFi",
"=CapturePCM/Mic/HiFi",
"=JackDev/Mic/HiFi",
@@ -1173,19 +1175,20 @@
"2",
"HP-L,HP-R",
- "hw:my-sound-card,0",
+ "hw:my-sound-card,1",
"SPK-L,SPK-R",
-
"hw:my-sound-card,0",
+
+ "hw:my-sound-card,2",
"my-sound-card Headset Jack",
"0",
"CAPTURE",
- "hw:my-sound-card,0",
+ "hw:my-sound-card,3",
"MIC-L,MIC-R",
"-10",
- "hw:my-sound-card,2",
+ "hw:my-sound-card,4",
"HDMI",
};
@@ -1206,6 +1209,7 @@
// Headphone
section = sections;
+ EXPECT_EQ(0, strcmp(section->pcm_name, "hw:my-sound-card,0"));
EXPECT_EQ(0, strcmp(section->name, "Headphone"));
EXPECT_EQ(0, section->dev_idx);
EXPECT_EQ(CRAS_STREAM_OUTPUT, section->dir);
@@ -1219,16 +1223,19 @@
EXPECT_EQ(0, strcmp(m_name->name, "HP-R"));
EXPECT_EQ(NULL, m_name->next);
EXPECT_EQ(2, section->jack_switch);
+ dev_idx = section->dev_idx;
// Speaker
section = section->next;
+ EXPECT_EQ(0, strcmp(section->pcm_name, "hw:my-sound-card,1"));
EXPECT_EQ(0, strcmp(section->name, "Speaker"));
- EXPECT_EQ(0, section->dev_idx);
+ EXPECT_EQ(1, section->dev_idx);
EXPECT_EQ(CRAS_STREAM_OUTPUT, section->dir);
EXPECT_EQ(NULL, section->jack_name);
EXPECT_EQ(NULL, section->jack_type);
EXPECT_EQ(-1, section->jack_switch);
EXPECT_EQ(NULL, section->mixer_name);
+ EXPECT_EQ(dev_idx, section->dependent_dev_idx);
ASSERT_NE((struct mixer_name*)NULL, section->coupled);
m_name = section->coupled;
EXPECT_EQ(0, strcmp(m_name->name, "SPK-L"));
@@ -1238,8 +1245,9 @@
// Mic
section = section->next;
+ EXPECT_EQ(0, strcmp(section->pcm_name, "hw:my-sound-card,2"));
EXPECT_EQ(0, strcmp(section->name, "Mic"));
- EXPECT_EQ(0, section->dev_idx);
+ EXPECT_EQ(2, section->dev_idx);
EXPECT_EQ(CRAS_STREAM_INPUT, section->dir);
EXPECT_EQ(0, strcmp(section->jack_name, values[1]));
EXPECT_EQ(0, strcmp(section->jack_type, "gpio"));
@@ -1250,8 +1258,9 @@
// Internal Mic
section = section->next;
+ EXPECT_EQ(0, strcmp(section->pcm_name, "hw:my-sound-card,3"));
EXPECT_EQ(0, strcmp(section->name, "Internal Mic"));
- EXPECT_EQ(0, section->dev_idx);
+ EXPECT_EQ(3, section->dev_idx);
EXPECT_EQ(CRAS_STREAM_INPUT, section->dir);
EXPECT_EQ(NULL, section->jack_name);
EXPECT_EQ(NULL, section->jack_type);
@@ -1265,8 +1274,9 @@
// HDMI
section = section->next;
+ EXPECT_EQ(0, strcmp(section->pcm_name, "hw:my-sound-card,4"));
EXPECT_EQ(0, strcmp(section->name, "HDMI"));
- EXPECT_EQ(2, section->dev_idx);
+ EXPECT_EQ(4, section->dev_idx);
EXPECT_EQ(CRAS_STREAM_OUTPUT, section->dir);
EXPECT_EQ(NULL, section->jack_name);
EXPECT_EQ(NULL, section->jack_type);
diff --git a/cras/src/tests/audio_thread_unittest.cc b/cras/src/tests/audio_thread_unittest.cc
index 0280a1e..6b78c69 100644
--- a/cras/src/tests/audio_thread_unittest.cc
+++ b/cras/src/tests/audio_thread_unittest.cc
@@ -18,6 +18,7 @@
#define FIRST_CB_LEVEL 480
static int cras_audio_thread_event_busyloop_called;
+static int cras_audio_thread_event_severe_underrun_called;
static unsigned int cras_rstream_dev_offset_called;
static unsigned int cras_rstream_dev_offset_ret[MAX_CALLS];
static const struct cras_rstream*
@@ -963,6 +964,7 @@
thread_add_stream(thread_, &rstream, &piodev, 1);
// Assume device is running and there is a severe underrun.
+ cras_audio_thread_event_severe_underrun_called = 0;
iodev.state = CRAS_IODEV_STATE_NORMAL_RUN;
frames_queued_ = -EPIPE;
@@ -975,6 +977,7 @@
// Audio thread should ask main thread to reset device.
EXPECT_EQ(1, cras_iodev_reset_request_called);
EXPECT_EQ(&iodev, cras_iodev_reset_request_iodev);
+ EXPECT_EQ(1, cras_audio_thread_event_severe_underrun_called);
thread_rm_open_dev(thread_, CRAS_STREAM_OUTPUT, iodev.info.idx);
TearDownRstream(&rstream);
@@ -1444,6 +1447,11 @@
return 0;
}
+int cras_audio_thread_event_severe_underrun() {
+ cras_audio_thread_event_severe_underrun_called++;
+ return 0;
+}
+
float input_data_get_software_gain_scaler(struct input_data* data,
float idev_sw_gain_scaler,
struct cras_rstream* stream) {
diff --git a/cras/src/tests/control_rclient_unittest.cc b/cras/src/tests/control_rclient_unittest.cc
index 189d22a..b0d01fc 100644
--- a/cras/src/tests/control_rclient_unittest.cc
+++ b/cras/src/tests/control_rclient_unittest.cc
@@ -9,6 +9,7 @@
extern "C" {
#include "audio_thread.h"
#include "cras_bt_log.h"
+#include "cras_main_thread_log.h"
#include "cras_messages.h"
#include "cras_rclient.h"
#include "cras_rstream.h"
@@ -159,6 +160,7 @@
connect_msg_.dev_idx = NO_DEVICE;
connect_msg_.client_shm_size = 0;
btlog = cras_bt_event_log_init();
+ main_log = main_thread_event_log_init();
ResetStubData();
}
@@ -168,6 +170,7 @@
close(pipe_fds_[0]);
close(pipe_fds_[1]);
cras_bt_event_log_deinit(btlog);
+ main_thread_event_log_deinit(main_log);
}
void RegisterNotification(enum CRAS_CLIENT_MESSAGE_ID msg_id,
@@ -734,6 +737,7 @@
extern "C" {
struct cras_bt_event_log* btlog;
+struct main_thread_event_log* main_log;
struct audio_thread* cras_iodev_list_get_audio_thread() {
return iodev_get_thread_return;
diff --git a/cras/src/tests/dev_io_unittest.cc b/cras/src/tests/dev_io_unittest.cc
index 02dc638..096e3ed 100644
--- a/cras/src/tests/dev_io_unittest.cc
+++ b/cras/src/tests/dev_io_unittest.cc
@@ -137,6 +137,49 @@
}
/*
+ * If any hw_level is larger than 0.5 * buffer_size and
+ * DROP_FRAMES_THRESHOLD_MS, reset all input devices.
+ */
+
+TEST_F(DevIoSuite, SendCapturedNeedToResetDevices2) {
+ struct timespec start;
+ struct timespec drop_time;
+ struct open_dev* dev_list = NULL;
+ bool rc;
+
+ stream = create_stream(1, 1, CRAS_STREAM_INPUT, 2000, &format);
+
+ clock_gettime(CLOCK_MONOTONIC_RAW, &start);
+ AddFakeDataToStream(stream.get(), 0);
+
+ DevicePtr dev1 =
+ create_device(CRAS_STREAM_INPUT, 2048, &format, CRAS_NODE_TYPE_MIC);
+ DevicePtr dev2 =
+ create_device(CRAS_STREAM_INPUT, 10000, &format, CRAS_NODE_TYPE_MIC);
+ DL_APPEND(dev_list, dev1->odev.get());
+ DL_APPEND(dev_list, dev2->odev.get());
+ add_stream_to_dev(dev1->dev, stream);
+ add_stream_to_dev(dev2->dev, stream);
+
+ iodev_stub_frames_queued(dev1->dev.get(), 2480, start);
+ iodev_stub_frames_queued(dev2->dev.get(), 2480, start);
+ EXPECT_EQ(0, dev_io_send_captured_samples(dev_list));
+
+ /*
+ * Should drop frames to one min_cb_level, which is 2480 - 2000 = 480 (10ms).
+ */
+ rc = iodev_stub_get_drop_time(dev1->dev.get(), &drop_time);
+ EXPECT_EQ(true, rc);
+ EXPECT_EQ(0, drop_time.tv_sec);
+ EXPECT_EQ(10000000, drop_time.tv_nsec);
+
+ rc = iodev_stub_get_drop_time(dev2->dev.get(), &drop_time);
+ EXPECT_EQ(true, rc);
+ EXPECT_EQ(0, drop_time.tv_sec);
+ EXPECT_EQ(10000000, drop_time.tv_nsec);
+}
+
+/*
* If the hw_level is larger than 1.5 * largest_cb_level but less than
* DROP_FRAMES_THRESHOLD_MS, do nothing.
*/
@@ -161,7 +204,10 @@
EXPECT_EQ(false, rc);
}
-/* If all hw_level is less than 1.5 * largest_cb_level, do nothing. */
+/*
+ * If all hw_level is less than 1.5 * largest_cb_level and 0.5 * buffer_size,
+ * do nothing.
+ */
TEST_F(DevIoSuite, SendCapturedNoNeedToResetDevices) {
struct timespec start;
struct timespec drop_time;
@@ -263,6 +309,10 @@
return 0;
}
+int cras_audio_thread_event_severe_underrun() {
+ return 0;
+}
+
int dev_stream_attached_devs(const struct dev_stream* dev_stream) {
return 0;
}
diff --git a/cras/src/tests/fmt_conv_ops_unittest.cc b/cras/src/tests/fmt_conv_ops_unittest.cc
index a76087f..d8feeab 100644
--- a/cras/src/tests/fmt_conv_ops_unittest.cc
+++ b/cras/src/tests/fmt_conv_ops_unittest.cc
@@ -619,10 +619,10 @@
EXPECT_EQ(ret, frames);
for (size_t i = 0; i < frames; ++i) {
+ int32_t sum = 0;
for (size_t k = 0; k < in_ch; ++k)
- src[i * in_ch + k] /= in_ch;
- for (size_t k = 1; k < in_ch; ++k)
- src[i * in_ch + 0] += src[i * in_ch + k];
+ sum += (int32_t)src[i * in_ch + k];
+ src[i * in_ch + 0] = (int16_t)(sum / (int32_t)in_ch);
}
for (size_t i = 0; i < frames; ++i) {
for (size_t k = 0; k < out_ch; ++k)
@@ -630,6 +630,36 @@
}
}
+// Test 6ch to 8ch conversion. S16_LE.
+TEST(FormatConverterOpsTest, 6chTo8chS16LE) {
+ const size_t frames = 65536;
+ const size_t in_ch = 6;
+ const size_t out_ch = 8;
+ struct cras_audio_format fmt = {
+ .format = SND_PCM_FORMAT_S16_LE,
+ .frame_rate = 48000,
+ .num_channels = 8,
+ };
+
+ S16LEPtr src = CreateS16LE(frames * in_ch);
+ S16LEPtr dst = CreateS16LE(frames * out_ch);
+ for (size_t i = 0; i < frames; ++i) {
+ for (size_t k = 0; k < in_ch; k++) {
+ src[i * in_ch + k] = (k == 0) ? (INT16_MIN + (int16_t)i) : 0;
+ }
+ }
+
+ size_t ret = s16_default_all_to_all(&fmt, in_ch, out_ch, (uint8_t*)src.get(),
+ frames, (uint8_t*)dst.get());
+ EXPECT_EQ(ret, frames);
+
+ for (size_t i = 0; i < frames; ++i) {
+ src[i * in_ch + 0] /= (int16_t)in_ch;
+ for (size_t k = 0; k < out_ch; ++k)
+ EXPECT_EQ(src[i * in_ch + 0], dst[i * out_ch + k]);
+ }
+}
+
// Test Multiply with Coef. S16_LE.
TEST(FormatConverterOpsTest, MultiplyWithCoefS16LE) {
const size_t buf_size = 4096;
diff --git a/cras/src/tests/hfp_ag_profile_unittest.cc b/cras/src/tests/hfp_ag_profile_unittest.cc
index 2baea4f..1a115f0 100644
--- a/cras/src/tests/hfp_ag_profile_unittest.cc
+++ b/cras/src/tests/hfp_ag_profile_unittest.cc
@@ -255,6 +255,10 @@
return 0;
}
+int hfp_slc_get_hf_battery_level(struct hfp_slc_handle* handle) {
+ return -1;
+}
+
struct cras_bt_device* cras_a2dp_connected_device() {
return NULL;
}
@@ -266,6 +270,10 @@
void cras_a2dp_suspend_connected_device(struct cras_bt_device* device) {}
+const char* cras_bt_device_address(const struct cras_bt_device* device) {
+ return "";
+}
+
int cras_bt_device_audio_gateway_initialized(struct cras_bt_device* device) {
return 0;
}
@@ -278,6 +286,11 @@
cras_bt_device_notify_profile_dropped_profile = profile;
}
+void cras_observer_notify_bt_battery_changed(const char* address,
+ uint32_t level) {
+ return;
+}
+
bool cras_system_get_bt_wbs_enabled() {
return true;
}
diff --git a/cras/src/tests/hfp_alsa_iodev_unittest.cc b/cras/src/tests/hfp_alsa_iodev_unittest.cc
index 73e9638..8975694 100644
--- a/cras/src/tests/hfp_alsa_iodev_unittest.cc
+++ b/cras/src/tests/hfp_alsa_iodev_unittest.cc
@@ -22,6 +22,7 @@
static struct cras_iodev fake_sco_out, fake_sco_in;
static struct cras_bt_device* fake_device;
static struct hfp_slc_handle* fake_slc;
+static struct cras_audio_format fake_format;
static size_t cras_bt_device_append_iodev_called;
static size_t cras_bt_device_rm_iodev_called;
@@ -240,13 +241,23 @@
TEST_F(HfpAlsaIodev, ConfigureDev) {
struct cras_iodev* iodev;
size_t buf_size = 8192;
+ struct hfp_alsa_io* hfp_alsa_io;
fake_sco_out.direction = CRAS_STREAM_OUTPUT;
fake_sco_out.buffer_size = buf_size;
iodev = hfp_alsa_iodev_create(&fake_sco_out, fake_device, fake_slc,
CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY);
+ hfp_alsa_io = (struct hfp_alsa_io*)iodev;
+ iodev->format = &fake_format;
iodev->configure_dev(iodev);
+ EXPECT_EQ(fake_format.num_channels, hfp_alsa_io->aio->format->num_channels);
+ EXPECT_EQ(fake_format.frame_rate, hfp_alsa_io->aio->format->frame_rate);
+ EXPECT_EQ(fake_format.format, hfp_alsa_io->aio->format->format);
+ for (int i = 0; i < CRAS_CH_MAX; i++)
+ EXPECT_EQ(fake_format.channel_layout[i],
+ 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(buf_size, iodev->buffer_size);
diff --git a/cras/src/tests/hfp_info_unittest.cc b/cras/src/tests/hfp_info_unittest.cc
index b5e7b25..48a1c89 100644
--- a/cras/src/tests/hfp_info_unittest.cc
+++ b/cras/src/tests/hfp_info_unittest.cc
@@ -346,10 +346,11 @@
hci_sco_buf[1] = headers[seq % 4];
+ /* Assume typical 60 bytes case. */
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
iov.iov_base = hci_sco_buf;
- iov.iov_len = HCI_SCO_PKT_SIZE;
+ iov.iov_len = 60;
msg.msg_control = control;
msg.msg_controllen = control_size;
diff --git a/cras/src/tests/hfp_slc_unittest.cc b/cras/src/tests/hfp_slc_unittest.cc
index 8e78727..4501b07 100644
--- a/cras/src/tests/hfp_slc_unittest.cc
+++ b/cras/src/tests/hfp_slc_unittest.cc
@@ -61,6 +61,8 @@
char* chp;
ResetStubData();
+ btlog = cras_bt_event_log_init();
+
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sock));
handle = hfp_slc_create(sock[0], 0, AG_ENHANCED_CALL_STATUS, device,
slc_initialized_cb, slc_disconnected_cb);
@@ -110,6 +112,7 @@
ASSERT_EQ(1, cras_bt_device_update_hardware_volume_called);
hfp_slc_destroy(handle);
+ cras_bt_event_log_deinit(btlog);
}
TEST(HfpSlc, DisconnectSlc) {
diff --git a/cras/src/tests/iodev_list_unittest.cc b/cras/src/tests/iodev_list_unittest.cc
index f9644fa..d40faca 100644
--- a/cras/src/tests/iodev_list_unittest.cc
+++ b/cras/src/tests/iodev_list_unittest.cc
@@ -12,6 +12,7 @@
#include "audio_thread.h"
#include "cras_iodev.h"
#include "cras_iodev_list.h"
+#include "cras_main_thread_log.h"
#include "cras_observer_ops.h"
#include "cras_ramp.h"
#include "cras_rstream.h"
@@ -83,6 +84,7 @@
static size_t cras_observer_notify_input_node_gain_called;
static int cras_iodev_open_called;
static int cras_iodev_open_ret[8];
+static struct cras_audio_format cras_iodev_open_fmt;
static int set_mute_called;
static std::vector<struct cras_iodev*> set_mute_dev_vector;
static std::vector<unsigned int> audio_thread_dev_start_ramp_dev_vector;
@@ -129,6 +131,10 @@
channel_counts_[0] = 2;
channel_counts_[1] = 0;
+ fmt_.format = SND_PCM_FORMAT_S16_LE;
+ fmt_.frame_rate = 48000;
+ fmt_.num_channels = 2;
+
memset(&d1_, 0, sizeof(d1_));
memset(&d2_, 0, sizeof(d2_));
memset(&d3_, 0, sizeof(d3_));
@@ -234,7 +240,9 @@
dummy_hotword_iodev.update_active_node = update_active_node;
}
- virtual void TearDown() { cras_iodev_list_reset(); }
+ virtual void TearDown() {
+ cras_iodev_list_reset();
+ }
static void set_volume_1(struct cras_iodev* iodev) { set_volume_1_called_++; }
@@ -266,6 +274,7 @@
struct cras_iodev d1_;
struct cras_iodev d2_;
struct cras_iodev d3_;
+ struct cras_audio_format fmt_;
size_t sample_rates_[3];
size_t channel_counts_[2];
static int set_volume_1_called_;
@@ -303,6 +312,8 @@
rc = cras_iodev_list_add_output(&d1_);
ASSERT_EQ(0, rc);
+ d1_.format = &fmt_;
+
audio_thread_add_open_dev_called = 0;
cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
cras_make_node_id(d1_.info.idx, 1));
@@ -348,6 +359,71 @@
EXPECT_EQ(3, cras_observer_notify_active_node_called);
}
+/* Check that the suspend/resume call of active iodev will be triggered and
+ * fallback device will be transciently enabled while adding a new stream whose
+ * channel count is higher than the active iodev. */
+TEST_F(IoDevTestSuite, ReopenDevForHigherChannels) {
+ struct cras_rstream rstream, rstream2;
+ struct cras_rstream* stream_list = NULL;
+ int rc;
+
+ memset(&rstream, 0, sizeof(rstream));
+ memset(&rstream2, 0, sizeof(rstream2));
+ rstream.format = fmt_;
+ rstream2.format = fmt_;
+ rstream2.format.num_channels = 6;
+
+ cras_iodev_list_init();
+
+ d1_.direction = CRAS_STREAM_OUTPUT;
+ rc = cras_iodev_list_add_output(&d1_);
+ ASSERT_EQ(0, rc);
+
+ d1_.format = &fmt_;
+ d1_.info.max_supported_channels = 2;
+
+ audio_thread_add_open_dev_called = 0;
+ cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
+ cras_make_node_id(d1_.info.idx, 1));
+ DL_APPEND(stream_list, &rstream);
+ stream_list_get_ret = stream_list;
+ stream_add_cb(&rstream);
+ EXPECT_EQ(1, audio_thread_add_stream_called);
+ EXPECT_EQ(1, audio_thread_add_open_dev_called);
+ EXPECT_EQ(1, cras_iodev_open_called);
+ EXPECT_EQ(2, cras_iodev_open_fmt.num_channels);
+
+ audio_thread_add_stream_called = 0;
+ audio_thread_add_open_dev_called = 0;
+ cras_iodev_open_called = 0;
+
+ /* stream_list should be descending ordered by channel count. */
+ DL_PREPEND(stream_list, &rstream2);
+ stream_list_get_ret = stream_list;
+ stream_add_cb(&rstream2);
+ /* The channel count(=6) of rstream2 exceeds d1's max_supported_channels(=2),
+ * rstream2 will be added directly to d1, which will not be re-opened. */
+ EXPECT_EQ(1, audio_thread_add_stream_called);
+ EXPECT_EQ(0, audio_thread_add_open_dev_called);
+ EXPECT_EQ(0, cras_iodev_open_called);
+
+ d1_.info.max_supported_channels = 6;
+ stream_rm_cb(&rstream2);
+
+ audio_thread_add_stream_called = 0;
+ audio_thread_add_open_dev_called = 0;
+ cras_iodev_open_called = 0;
+
+ stream_add_cb(&rstream2);
+ /* Added both rstreams to fallback device, then re-opened d1. */
+ EXPECT_EQ(4, audio_thread_add_stream_called);
+ EXPECT_EQ(2, audio_thread_add_open_dev_called);
+ EXPECT_EQ(2, cras_iodev_open_called);
+ EXPECT_EQ(6, cras_iodev_open_fmt.num_channels);
+
+ cras_iodev_list_deinit();
+}
+
/* Check that after resume, all output devices enter ramp mute state if there is
* any output stream. */
TEST_F(IoDevTestSuite, RampMuteAfterResume) {
@@ -369,6 +445,9 @@
rc = cras_iodev_list_add_input(&d2_);
ASSERT_EQ(0, rc);
+ d1_.format = &fmt_;
+ d2_.format = &fmt_;
+
audio_thread_add_open_dev_called = 0;
cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
cras_make_node_id(d1_.info.idx, 1));
@@ -424,6 +503,8 @@
rc = cras_iodev_list_add_output(&d1_);
ASSERT_EQ(0, rc);
+ d1_.format = &fmt_;
+
cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
cras_make_node_id(d1_.info.idx, 0));
@@ -457,6 +538,9 @@
rc = cras_iodev_list_add_input(&d2_);
ASSERT_EQ(0, rc);
+ d1_.format = &fmt_;
+ d2_.format = &fmt_;
+
cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
cras_make_node_id(d1_.info.idx, 0));
/* No close call happened, because no stream exists. */
@@ -502,6 +586,9 @@
rc = cras_iodev_list_add_output(&d2_);
ASSERT_EQ(0, rc);
+ d1_.format = &fmt_;
+ d2_.format = &fmt_;
+
cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
cras_make_node_id(d1_.info.idx, 1));
DL_APPEND(stream_list, &rstream);
@@ -571,12 +658,15 @@
struct cras_rstream* stream_list = NULL;
memset(&rstream, 0, sizeof(rstream));
+ rstream.format = fmt_;
cras_iodev_list_init();
d1_.direction = CRAS_STREAM_OUTPUT;
rc = cras_iodev_list_add_output(&d1_);
ASSERT_EQ(0, rc);
+ d1_.format = &fmt_;
+
cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
cras_make_node_id(d1_.info.idx, 0));
@@ -603,6 +693,7 @@
EXPECT_EQ(1, cras_tm_create_timer_called);
EXPECT_EQ(1, audio_thread_add_stream_called);
+ dummy_empty_iodev[CRAS_STREAM_OUTPUT].format = &fmt_;
cras_tm_timer_cb = NULL;
cras_iodev_open_ret[3] = -5;
stream_add_cb(&rstream);
@@ -626,6 +717,8 @@
rc = cras_iodev_list_add_output(&d1_);
ASSERT_EQ(0, rc);
+ d1_.format = &fmt_;
+
rstream.is_pinned = 1;
rstream.pinned_dev_idx = d1_.info.idx;
@@ -681,6 +774,9 @@
rc = cras_iodev_list_add_output(&d2_);
ASSERT_EQ(0, rc);
+ d1_.format = &fmt_;
+ d2_.format = &fmt_;
+
audio_thread_add_open_dev_called = 0;
audio_thread_rm_open_dev_called = 0;
@@ -751,6 +847,9 @@
rc = cras_iodev_list_add_output(&d2_);
ASSERT_EQ(0, rc);
+ d1_.format = &fmt_;
+ d2_.format = &fmt_;
+
audio_thread_add_open_dev_called = 0;
audio_thread_rm_open_dev_called = 0;
device_enabled_count = 0;
@@ -883,6 +982,7 @@
TEST_F(IoDevTestSuite, AddRemoveOutput) {
struct cras_iodev_info* dev_info;
int rc;
+ cras_iodev_list_init();
rc = cras_iodev_list_add_output(&d1_);
EXPECT_EQ(0, rc);
@@ -914,6 +1014,7 @@
EXPECT_EQ(0, rc);
free(dev_info);
EXPECT_EQ(0, cras_observer_notify_active_node_called);
+ cras_iodev_list_deinit();
}
// Test output_mute_changed callback.
@@ -992,39 +1093,56 @@
// Test enable/disable an iodev.
TEST_F(IoDevTestSuite, EnableDisableDevice) {
+ struct cras_rstream rstream;
+ cras_iodev_list_init();
device_enabled_count = 0;
device_disabled_count = 0;
+ memset(&rstream, 0, sizeof(rstream));
EXPECT_EQ(0, cras_iodev_list_add_output(&d1_));
EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(
device_enabled_cb, device_disabled_cb, (void*)0xABCD));
- // Enable a device.
+ // Enable a device, fallback should be diabled accordingly.
cras_iodev_list_enable_dev(&d1_);
EXPECT_EQ(&d1_, device_enabled_dev);
EXPECT_EQ((void*)0xABCD, device_enabled_cb_data);
EXPECT_EQ(1, device_enabled_count);
+ EXPECT_EQ(1, device_disabled_count);
EXPECT_EQ(&d1_, cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT));
- // Disable a device.
+ // Connect a normal stream.
+ cras_iodev_open_called = 0;
+ stream_add_cb(&rstream);
+ EXPECT_EQ(1, cras_iodev_open_called);
+
+ stream_list_has_pinned_stream_ret[d1_.info.idx] = 0;
+ // Disable a device. Expect dev is closed because there's no pinned stream.
+ update_active_node_called = 0;
cras_iodev_list_disable_dev(&d1_, false);
EXPECT_EQ(&d1_, device_disabled_dev);
- EXPECT_EQ(1, device_disabled_count);
+ EXPECT_EQ(2, device_disabled_count);
EXPECT_EQ((void*)0xABCD, device_disabled_cb_data);
+ EXPECT_EQ(1, audio_thread_rm_open_dev_called);
+ EXPECT_EQ(1, cras_iodev_close_called);
+ EXPECT_EQ(&d1_, cras_iodev_close_dev);
+ EXPECT_EQ(1, update_active_node_called);
+
EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(
device_enabled_cb, device_disabled_cb, (void*)0xCDEF));
EXPECT_EQ(2, cras_observer_notify_active_node_called);
EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(NULL, NULL, NULL));
+ cras_iodev_list_deinit();
}
// Test adding/removing an input dev to the list.
TEST_F(IoDevTestSuite, AddRemoveInput) {
struct cras_iodev_info* dev_info;
int rc, i;
- uint32_t found_mask;
+ uint64_t found_mask;
d1_.direction = CRAS_STREAM_INPUT;
d2_.direction = CRAS_STREAM_INPUT;
@@ -1057,8 +1175,8 @@
found_mask = 0;
for (i = 0; i < rc; i++) {
uint32_t idx = dev_info[i].idx;
- EXPECT_EQ(0, (found_mask & (1 << idx)));
- found_mask |= (1 << idx);
+ EXPECT_EQ(0, (found_mask & (static_cast<uint64_t>(1) << idx)));
+ found_mask |= (static_cast<uint64_t>(1) << idx);
}
}
if (rc > 0)
@@ -1094,6 +1212,7 @@
d2_.direction = CRAS_STREAM_INPUT;
server_state_update_begin_return = NULL;
+ cras_iodev_list_init();
rc = cras_iodev_list_add_input(&d1_);
EXPECT_EQ(0, rc);
@@ -1104,6 +1223,7 @@
EXPECT_EQ(0, cras_iodev_list_rm_input(&d1_));
EXPECT_EQ(0, cras_iodev_list_rm_input(&d2_));
+ cras_iodev_list_deinit();
}
// Test removing the last input.
@@ -1317,6 +1437,10 @@
rc = cras_iodev_list_add_output(&d3_);
ASSERT_EQ(0, rc);
+ d1_.format = &fmt_;
+ d2_.format = &fmt_;
+ d3_.format = &fmt_;
+
audio_thread_add_open_dev_called = 0;
cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
cras_make_node_id(d3_.info.idx, 1));
@@ -1353,6 +1477,57 @@
cras_iodev_list_deinit();
}
+TEST_F(IoDevTestSuite, OutputDevIdleClose) {
+ int rc;
+ struct cras_rstream rstream;
+
+ memset(&rstream, 0, sizeof(rstream));
+ cras_iodev_list_init();
+
+ d1_.direction = CRAS_STREAM_OUTPUT;
+ rc = cras_iodev_list_add_output(&d1_);
+ EXPECT_EQ(0, rc);
+
+ d1_.format = &fmt_;
+
+ audio_thread_add_open_dev_called = 0;
+ cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
+ cras_make_node_id(d1_.info.idx, 1));
+ EXPECT_EQ(0, audio_thread_add_open_dev_called);
+ EXPECT_EQ(0, audio_thread_rm_open_dev_called);
+
+ // If a stream is added, the device should be opened.
+ stream_add_cb(&rstream);
+ EXPECT_EQ(1, audio_thread_add_open_dev_called);
+
+ audio_thread_rm_open_dev_called = 0;
+ audio_thread_drain_stream_return = 0;
+ clock_gettime_retspec.tv_sec = 15;
+ stream_rm_cb(&rstream);
+ EXPECT_EQ(1, audio_thread_drain_stream_called);
+ EXPECT_EQ(0, audio_thread_rm_open_dev_called);
+ EXPECT_EQ(1, cras_tm_create_timer_called);
+
+ // Expect no rm dev happen because idle time not yet expire, and
+ // new timer should be scheduled for the rest of the idle time.
+ clock_gettime_retspec.tv_sec += 7;
+ cras_tm_timer_cb(NULL, NULL);
+ EXPECT_EQ(0, audio_thread_rm_open_dev_called);
+ EXPECT_EQ(2, cras_tm_create_timer_called);
+
+ // Expect d1_ be closed upon unplug, and the timer stay armed.
+ cras_iodev_list_rm_output(&d1_);
+ EXPECT_EQ(1, audio_thread_rm_open_dev_called);
+ EXPECT_EQ(0, cras_tm_cancel_timer_called);
+
+ // When timer eventually fired expect there's no more new
+ // timer scheduled because d1_ has closed already.
+ clock_gettime_retspec.tv_sec += 4;
+ cras_tm_timer_cb(NULL, NULL);
+ EXPECT_EQ(2, cras_tm_create_timer_called);
+ cras_iodev_list_deinit();
+}
+
TEST_F(IoDevTestSuite, DrainTimerCancel) {
int rc;
struct cras_rstream rstream;
@@ -1365,6 +1540,8 @@
rc = cras_iodev_list_add_output(&d1_);
EXPECT_EQ(0, rc);
+ d1_.format = &fmt_;
+
audio_thread_add_open_dev_called = 0;
cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
cras_make_node_id(d1_.info.idx, 1));
@@ -1429,6 +1606,107 @@
cras_iodev_list_deinit();
}
+TEST_F(IoDevTestSuite, CloseDevWithPinnedStream) {
+ int rc;
+ struct cras_rstream rstream1, rstream2;
+ cras_iodev_list_init();
+
+ d1_.direction = CRAS_STREAM_OUTPUT;
+ d1_.info.idx = 1;
+ rc = cras_iodev_list_add_output(&d1_);
+ EXPECT_EQ(0, rc);
+
+ memset(&rstream1, 0, sizeof(rstream1));
+ memset(&rstream2, 0, sizeof(rstream2));
+ rstream2.is_pinned = 1;
+ rstream2.pinned_dev_idx = d1_.info.idx;
+
+ d1_.format = &fmt_;
+ audio_thread_add_open_dev_called = 0;
+ EXPECT_EQ(0, audio_thread_add_open_dev_called);
+ EXPECT_EQ(0, audio_thread_rm_open_dev_called);
+
+ // Add a normal stream
+ stream_add_cb(&rstream1);
+ EXPECT_EQ(1, audio_thread_add_open_dev_called);
+
+ // Add a pinned stream, expect another dev open call triggered.
+ cras_iodev_open_called = 0;
+ stream_add_cb(&rstream2);
+ EXPECT_EQ(1, cras_iodev_open_called);
+
+ // Force disable d1_ and make sure d1_ gets closed.
+ audio_thread_rm_open_dev_called = 0;
+ update_active_node_called = 0;
+ cras_iodev_close_called = 0;
+ cras_iodev_list_disable_dev(&d1_, 1);
+ EXPECT_EQ(1, audio_thread_rm_open_dev_called);
+ EXPECT_EQ(1, cras_iodev_close_called);
+ EXPECT_EQ(&d1_, cras_iodev_close_dev);
+ EXPECT_EQ(1, update_active_node_called);
+
+ // Add back the two streams, one normal one pinned.
+ audio_thread_add_open_dev_called = 0;
+ audio_thread_rm_open_dev_called = 0;
+ cras_iodev_open_called = 0;
+ stream_add_cb(&rstream2);
+ EXPECT_EQ(1, audio_thread_add_open_dev_called);
+ EXPECT_EQ(1, cras_iodev_open_called);
+ stream_add_cb(&rstream1);
+
+ // Suspend d1_ and make sure d1_ gets closed.
+ update_active_node_called = 0;
+ cras_iodev_close_called = 0;
+ cras_iodev_list_suspend_dev(d1_.info.idx);
+ EXPECT_EQ(1, audio_thread_rm_open_dev_called);
+ EXPECT_EQ(1, cras_iodev_close_called);
+ EXPECT_EQ(&d1_, cras_iodev_close_dev);
+ EXPECT_EQ(1, update_active_node_called);
+
+ cras_iodev_list_resume_dev(d1_.info.idx);
+
+ cras_iodev_list_deinit();
+}
+
+TEST_F(IoDevTestSuite, DisableDevWithPinnedStream) {
+ int rc;
+ struct cras_rstream rstream1;
+ cras_iodev_list_init();
+
+ d1_.direction = CRAS_STREAM_OUTPUT;
+ rc = cras_iodev_list_add_output(&d1_);
+ EXPECT_EQ(0, rc);
+
+ memset(&rstream1, 0, sizeof(rstream1));
+ rstream1.is_pinned = 1;
+ rstream1.pinned_dev_idx = d1_.info.idx;
+
+ d1_.format = &fmt_;
+ audio_thread_add_open_dev_called = 0;
+ cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
+ cras_make_node_id(d1_.info.idx, 1));
+ EXPECT_EQ(0, audio_thread_add_open_dev_called);
+ EXPECT_EQ(0, audio_thread_rm_open_dev_called);
+
+ // Add a pinned stream.
+ cras_iodev_open_called = 0;
+ stream_add_cb(&rstream1);
+ EXPECT_EQ(1, audio_thread_add_open_dev_called);
+ EXPECT_EQ(1, cras_iodev_open_called);
+
+ // Disable d1_ expect no close dev triggered because pinned stream.
+ stream_list_has_pinned_stream_ret[d1_.info.idx] = 1;
+ audio_thread_rm_open_dev_called = 0;
+ update_active_node_called = 0;
+ cras_iodev_close_called = 0;
+ cras_iodev_list_disable_dev(&d1_, 0);
+ EXPECT_EQ(0, audio_thread_rm_open_dev_called);
+ EXPECT_EQ(0, cras_iodev_close_called);
+ EXPECT_EQ(0, update_active_node_called);
+
+ cras_iodev_list_deinit();
+}
+
TEST_F(IoDevTestSuite, AddRemovePinnedStream) {
struct cras_rstream rstream;
@@ -1447,6 +1725,9 @@
d2_.info.idx = 2;
EXPECT_EQ(0, cras_iodev_list_add_output(&d2_));
+ d1_.format = &fmt_;
+ d2_.format = &fmt_;
+
// Setup pinned stream.
memset(&rstream, 0, sizeof(rstream));
rstream.is_pinned = 1;
@@ -1506,6 +1787,9 @@
d2_.direction = CRAS_STREAM_OUTPUT;
EXPECT_EQ(0, cras_iodev_list_add_output(&d2_));
+ d1_.format = &fmt_;
+ d2_.format = &fmt_;
+
// Setup pinned stream.
memset(&rstream, 0, sizeof(rstream));
rstream.is_pinned = 1;
@@ -1561,6 +1845,8 @@
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;
@@ -1606,6 +1892,8 @@
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;
@@ -1665,6 +1953,7 @@
extern "C" {
// Stubs
+struct main_thread_event_log* main_log;
struct cras_server_state* cras_system_state_update_begin() {
return server_state_update_begin_return;
@@ -1787,6 +2076,8 @@
const struct cras_audio_format* fmt) {
if (cras_iodev_open_ret[cras_iodev_open_called] == 0)
iodev->state = CRAS_IODEV_STATE_OPEN;
+ cras_iodev_open_fmt = *fmt;
+ iodev->format = &cras_iodev_open_fmt;
return cras_iodev_open_ret[cras_iodev_open_called++];
}
@@ -1794,6 +2085,7 @@
iodev->state = CRAS_IODEV_STATE_CLOSE;
cras_iodev_close_called++;
cras_iodev_close_dev = iodev;
+ iodev->format = NULL;
return 0;
}
diff --git a/cras/src/tests/iodev_unittest.cc b/cras/src/tests/iodev_unittest.cc
index f16094d..03cfee6 100644
--- a/cras/src/tests/iodev_unittest.cc
+++ b/cras/src/tests/iodev_unittest.cc
@@ -9,6 +9,7 @@
#include "audio_thread_log.h"
#include "cras_audio_area.h"
#include "cras_iodev.h"
+#include "cras_main_thread_log.h"
#include "cras_ramp.h"
#include "cras_rstream.h"
#include "dev_stream.h"
@@ -267,9 +268,14 @@
iodev_.dsp_context = NULL;
cras_audio_format_set_channel_layout_called = 0;
+
+ main_log = main_thread_event_log_init();
}
- virtual void TearDown() { cras_iodev_free_format(&iodev_); }
+ virtual void TearDown() {
+ cras_iodev_free_format(&iodev_);
+ main_thread_event_log_deinit(main_log);
+ }
struct cras_iodev iodev_;
size_t sample_rates_[3];
@@ -1056,6 +1062,8 @@
struct cras_iodev iodev;
struct cras_ionode ionode, ionode2;
+ main_log = main_thread_event_log_init();
+
memset(&iodev, 0, sizeof(iodev));
memset(&ionode, 0, sizeof(ionode));
memset(&ionode2, 0, sizeof(ionode2));
@@ -1077,6 +1085,7 @@
EXPECT_EQ(1, cras_iodev_list_disable_dev_called);
cras_iodev_set_node_plugged(&ionode2, 0);
EXPECT_EQ(1, cras_iodev_list_disable_dev_called);
+ main_thread_event_log_deinit(main_log);
}
TEST(IoDev, AddRemoveNode) {
@@ -2397,6 +2406,8 @@
extern "C" {
+struct main_thread_event_log* main_log;
+
// From libpthread.
int pthread_create(pthread_t* thread,
const pthread_attr_t* attr,
@@ -2732,10 +2743,6 @@
void input_data_set_all_streams_read(struct input_data* data,
unsigned int nframes) {}
-int cras_audio_thread_event_severe_underrun() {
- return 0;
-}
-
int cras_audio_thread_event_underrun() {
return 0;
}
diff --git a/cras/src/tests/stream_list_unittest.cc b/cras/src/tests/stream_list_unittest.cc
index 8f3c2e3..40be35d 100644
--- a/cras/src/tests/stream_list_unittest.cc
+++ b/cras/src/tests/stream_list_unittest.cc
@@ -28,13 +28,16 @@
static unsigned int create_called;
static struct cras_rstream_config* create_config;
-static struct cras_rstream dummy_rstream;
static int create_rstream_cb(struct cras_rstream_config* stream_config,
struct cras_rstream** stream) {
create_called++;
create_config = stream_config;
- *stream = &dummy_rstream;
- dummy_rstream.stream_id = 0x3003;
+ *stream = (struct cras_rstream*)malloc(sizeof(struct cras_rstream));
+ (*stream)->stream_id = stream_config->stream_id;
+ (*stream)->direction = stream_config->direction;
+ if (stream_config->format)
+ (*stream)->format = *(stream_config->format);
+
return 0;
}
@@ -43,6 +46,7 @@
static void destroy_rstream_cb(struct cras_rstream* rstream) {
destroy_called++;
destroyed_stream = rstream;
+ free(rstream);
}
static void reset_test_data() {
@@ -57,6 +61,10 @@
struct cras_rstream* s1;
struct cras_rstream_config s1_config;
+ s1_config.stream_id = 0x3003;
+ s1_config.direction = CRAS_STREAM_OUTPUT;
+ s1_config.format = NULL;
+
reset_test_data();
l = stream_list_create(added_cb, removed_cb, create_rstream_cb,
destroy_rstream_cb, NULL);
@@ -72,6 +80,55 @@
stream_list_destroy(l);
}
+TEST(StreamList, AddInDescendingOrderByChannels) {
+ struct stream_list* l;
+ struct cras_rstream* s1;
+ struct cras_rstream* s2;
+ struct cras_rstream* s3;
+ struct cras_audio_format s1_format, s2_format, s3_format;
+ struct cras_rstream_config s1_config, s2_config, s3_config;
+
+ s1_config.stream_id = 0x4001;
+ s1_config.direction = CRAS_STREAM_INPUT;
+ s1_format.num_channels = 6;
+ s1_config.format = &s1_format;
+
+ s2_config.stream_id = 0x4002;
+ s2_config.direction = CRAS_STREAM_OUTPUT;
+ s2_format.num_channels = 8;
+ s2_config.format = &s2_format;
+
+ s3_config.stream_id = 0x4003;
+ s3_config.direction = CRAS_STREAM_OUTPUT;
+ s3_format.num_channels = 2;
+ s3_config.format = &s3_format;
+
+ 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(6, stream_list_get(l)->format.num_channels);
+
+ stream_list_add(l, &s2_config, &s2);
+ EXPECT_EQ(2, add_called);
+ EXPECT_EQ(2, create_called);
+ EXPECT_EQ(8, stream_list_get(l)->format.num_channels);
+ EXPECT_EQ(6, stream_list_get(l)->next->format.num_channels);
+
+ stream_list_add(l, &s3_config, &s3);
+ EXPECT_EQ(3, add_called);
+ EXPECT_EQ(3, create_called);
+ EXPECT_EQ(8, stream_list_get(l)->format.num_channels);
+ EXPECT_EQ(6, stream_list_get(l)->next->format.num_channels);
+ EXPECT_EQ(2, stream_list_get(l)->next->next->format.num_channels);
+ EXPECT_EQ(0, stream_list_rm(l, 0x4001));
+ EXPECT_EQ(0, stream_list_rm(l, 0x4002));
+ EXPECT_EQ(0, stream_list_rm(l, 0x4003));
+ stream_list_destroy(l);
+}
+
extern "C" {
struct cras_timer* cras_tm_create_timer(struct cras_tm* tm,
diff --git a/cras/src/tests/timing_unittest.cc b/cras/src/tests/timing_unittest.cc
index d0ecb81..8a2de65 100644
--- a/cras/src/tests/timing_unittest.cc
+++ b/cras/src/tests/timing_unittest.cc
@@ -1177,6 +1177,11 @@
int cras_audio_thread_event_drop_samples() {
return 0;
}
+
+int cras_audio_thread_event_severe_underrun() {
+ return 0;
+}
+
void* buffer_share_get_data(const struct buffer_share* mix, unsigned int id) {
return NULL;
};
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 6eaf4a6..947b945 100644
--- a/cras/src/tools/cras_test_client/cras_test_client.c
+++ b/cras/src/tools/cras_test_client/cras_test_client.c
@@ -310,7 +310,7 @@
int rc = 0;
uint32_t frame_bytes = cras_client_format_bytes_per_frame(aud_format);
- rc = read(0, playback_samples, frames * frame_bytes);
+ rc = read(0, playback_samples, (size_t)frames * (size_t)frame_bytes);
if (rc <= 0) {
terminate_stream_loop();
return -1;
@@ -504,17 +504,17 @@
unsigned int data2 = log->log[tag_idx].data2;
unsigned int data3 = log->log[tag_idx].data3;
time_t lt;
- struct tm *t;
+ struct tm t;
/* Skip unused log entries. */
if (log->log[tag_idx].tag_sec == 0 && log->log[tag_idx].nsec == 0)
return;
- /* Convert from monotomic raw clock to realtime clock. */
+ /* Convert from monotonic raw clock to realtime clock. */
convert_time(&sec, &nsec, sec_offset, nsec_offset);
lt = sec;
- t = localtime(<);
- strftime(time_str, 128, "%Y-%m-%dT%H:%M:%S", t);
+ localtime_r(<, &t);
+ strftime(time_str, 128, "%Y-%m-%dT%H:%M:%S", &t);
printf("%s.%09u cras atlog ", time_str, nsec);
@@ -533,16 +533,16 @@
}
convert_time(&sec, &nsec, sec_offset, nsec_offset);
lt = sec;
- t = localtime(<);
- strftime(time_str, 128, " %H:%M:%S", t);
+ localtime_r(<, &t);
+ strftime(time_str, 128, " %H:%M:%S", &t);
switch (tag) {
case AUDIO_THREAD_WAKE:
printf("%-30s num_fds:%d\n", "WAKE", (int)data1);
break;
case AUDIO_THREAD_SLEEP:
- printf("%-30s sleep:%09d.%09d\n", "SLEEP", (int)data1,
- (int)data2);
+ printf("%-30s sleep:%09d.%09d non_empty %u\n", "SLEEP",
+ (int)data1, (int)data2, (int)data3);
break;
case AUDIO_THREAD_READ_AUDIO:
printf("%-30s dev:%u hw_level:%u read:%u\n", "READ_AUDIO",
@@ -775,8 +775,8 @@
for (i = 0; i < info->num_streams; i++) {
int channel;
- printf("stream: %llu dev: %u\n",
- (unsigned long long)info->streams[i].stream_id,
+ printf("stream: 0x%" PRIx64 " dev: %u\n",
+ info->streams[i].stream_id,
(unsigned int)info->streams[i].dev_idx);
printf("direction: %s\n",
(info->streams[i].direction == CRAS_STREAM_INPUT) ?
@@ -848,6 +848,92 @@
pthread_mutex_unlock(&done_mutex);
}
+static void show_mainlog_tag(const struct main_thread_event_log *log,
+ unsigned int tag_idx, int32_t sec_offset,
+ int32_t nsec_offset)
+{
+ unsigned int tag = (log->log[tag_idx].tag_sec >> 24) & 0xff;
+ unsigned int sec = log->log[tag_idx].tag_sec & 0x00ffffff;
+ unsigned int nsec = log->log[tag_idx].nsec;
+ unsigned int data1 = log->log[tag_idx].data1;
+ unsigned int data2 = log->log[tag_idx].data2;
+ unsigned int data3 = log->log[tag_idx].data3;
+ time_t lt;
+ struct tm t;
+
+ /* Skip unused log entries. */
+ if (log->log[tag_idx].tag_sec == 0 && log->log[tag_idx].nsec == 0)
+ return;
+
+ /* Convert from monotomic raw clock to realtime clock. */
+ convert_time(&sec, &nsec, sec_offset, nsec_offset);
+ lt = sec;
+ localtime_r(<, &t);
+ strftime(time_str, 128, "%Y-%m-%dT%H:%M:%S", &t);
+
+ printf("%s.%09u cras mainlog ", time_str, nsec);
+
+ switch (tag) {
+ case MAIN_THREAD_DEV_CLOSE:
+ printf("%-30s dev %u\n", "DEV_CLOSE", data1);
+ break;
+ case MAIN_THREAD_DEV_DISABLE:
+ printf("%-30s dev %u force %u\n", "DEV_DISABLE", data1, data2);
+ break;
+ case MAIN_THREAD_DEV_INIT:
+ printf("%-30s dev %u ch %u rate %u\n", "DEV_INIT", data1, data2,
+ data3);
+ break;
+ case MAIN_THREAD_DEV_REOPEN:
+ printf("%-30s new ch %u old ch %u rate %u\n", "DEV_REOPEN",
+ data1, data2, data3);
+ break;
+ case MAIN_THREAD_ADD_ACTIVE_NODE:
+ printf("%-30s dev %u\n", "ADD_ACTIVE_NODE", data1);
+ break;
+ case MAIN_THREAD_SELECT_NODE:
+ printf("%-30s dev %u\n", "SELECT_NODE", data1);
+ break;
+ case MAIN_THREAD_ADD_TO_DEV_LIST:
+ printf("%-30s dev %u %s\n", "ADD_TO_DEV_LIST", data1,
+ (data2 == CRAS_STREAM_OUTPUT) ? "output" : "input");
+ break;
+ case MAIN_THREAD_NODE_PLUGGED:
+ printf("%-30s dev %u %s\n", "NODE_PLUGGED", data1,
+ data2 ? "plugged" : "unplugged");
+ break;
+ case MAIN_THREAD_INPUT_NODE_GAIN:
+ printf("%-30s dev %u gain %u\n", "INPUT_NODE_GAIN", data1,
+ data2);
+ break;
+ case MAIN_THREAD_OUTPUT_NODE_VOLUME:
+ printf("%-30s dev %u volume %u\n", "OUTPUT_NODE_VOLUME", data1,
+ data2);
+ break;
+ case MAIN_THREAD_SET_OUTPUT_USER_MUTE:
+ printf("%-30s mute %u\n", "SET_OUTPUT_USER_MUTE", data1);
+ break;
+ case MAIN_THREAD_RESUME_DEVS:
+ printf("RESUME_DEVS\n");
+ break;
+ case MAIN_THREAD_SUSPEND_DEVS:
+ printf("SUSPEND_DEVS\n");
+ break;
+ case MAIN_THREAD_STREAM_ADDED:
+ printf("%-30s %s stream 0x%x buffer frames %u\n",
+ "STREAM_ADDED",
+ (data2 == CRAS_STREAM_OUTPUT ? "output" : "input"),
+ data1, data3);
+ break;
+ case MAIN_THREAD_STREAM_REMOVED:
+ printf("%-30s stream 0x%x\n", "STREAM_REMOVED", data1);
+ break;
+ default:
+ printf("%-30s\n", "UNKNOWN");
+ break;
+ }
+}
+
static void show_btlog_tag(const struct cras_bt_event_log *log,
unsigned int tag_idx, int32_t sec_offset,
int32_t nsec_offset)
@@ -858,17 +944,17 @@
unsigned int data1 = log->log[tag_idx].data1;
unsigned int data2 = log->log[tag_idx].data2;
time_t lt;
- struct tm *t;
+ struct tm t;
/* Skip unused log entries. */
if (log->log[tag_idx].tag_sec == 0 && log->log[tag_idx].nsec == 0)
return;
- /* Convert from monotomic raw clock to realtime clock. */
+ /* Convert from monotonic raw clock to realtime clock. */
convert_time(&sec, &nsec, sec_offset, nsec_offset);
lt = sec;
- t = localtime(<);
- strftime(time_str, 128, "%Y-%m-%dT%H:%M:%S", t);
+ localtime_r(<, &t);
+ strftime(time_str, 128, "%Y-%m-%dT%H:%M:%S", &t);
printf("%s.%09u cras btlog ", time_str, nsec);
@@ -905,7 +991,7 @@
data2);
break;
case BT_DEV_CONNECTED_CHANGE:
- printf("%-30s profiles %u now %u\n", "DEV_CONENCTED_CHANGE",
+ printf("%-30s profiles %u now %u\n", "DEV_CONNECTED_CHANGE",
data1, data2);
break;
case BT_DEV_CONN_WATCH_CB:
@@ -919,6 +1005,15 @@
case BT_HFP_HF_INDICATOR:
printf("%-30s HF read AG %s indicator\n", "HFP_HF_INDICATOR",
data1 ? "enabled" : "supported");
+ break;
+ case BT_HFP_SET_SPEAKER_GAIN:
+ printf("%-30s HF set speaker gain %u\n", "HFP_SET_SPEAKER_GAIN",
+ data1);
+ break;
+ case BT_HFP_UPDATE_SPEAKER_GAIN:
+ printf("%-30s HF update speaker gain %u\n",
+ "HFP_UPDATE_SPEAKER_GAIN", data1);
+ break;
case BT_HFP_NEW_CONNECTION:
printf("%-30s\n", "HFP_NEW_CONNECTION");
break;
@@ -953,6 +1048,12 @@
case BT_TRANSPORT_RELEASE:
printf("%-30s\n", "TRANSPORT_RELEASE");
break;
+ case BT_TRANSPORT_SET_VOLUME:
+ printf("%-30s %d\n", "TRANSPORT_SET_VOLUME", data1);
+ break;
+ case BT_TRANSPORT_UPDATE_VOLUME:
+ printf("%-30s %d\n", "TRANSPORT_UPDATE_VOLUME", data1);
+ break;
default:
printf("%-30s\n", "UNKNOWN");
break;
@@ -983,6 +1084,30 @@
pthread_mutex_unlock(&done_mutex);
}
+static void main_thread_debug_info(struct cras_client *client)
+{
+ const struct main_thread_debug_info *info;
+ time_t sec_offset;
+ int32_t nsec_offset;
+ int i, j;
+
+ info = cras_client_get_main_thread_debug_info(client);
+ fill_time_offset(&sec_offset, &nsec_offset);
+ j = info->main_log.write_pos;
+ i = 0;
+ printf("Main debug log:\n");
+ for (; i < info->main_log.len; i++) {
+ show_mainlog_tag(&info->main_log, j, sec_offset, nsec_offset);
+ j++;
+ j %= info->main_log.len;
+ }
+
+ /* Signal main thread we are done after the last chunk. */
+ pthread_mutex_lock(&done_mutex);
+ pthread_cond_signal(&done_cond);
+ pthread_mutex_unlock(&done_mutex);
+}
+
static void print_cras_audio_thread_snapshot(
const struct cras_audio_thread_snapshot *snapshot)
{
@@ -1416,6 +1541,22 @@
pthread_mutex_unlock(&done_mutex);
}
+static void show_main_thread_debug_info(struct cras_client *client)
+{
+ struct timespec wait_time;
+ cras_client_run_thread(client);
+ cras_client_connected_wait(client); /* To synchronize data. */
+ cras_client_update_main_thread_debug_info(client,
+ main_thread_debug_info);
+
+ clock_gettime(CLOCK_REALTIME, &wait_time);
+ wait_time.tv_sec += 2;
+
+ pthread_mutex_lock(&done_mutex);
+ pthread_cond_timedwait(&done_cond, &done_mutex, &wait_time);
+ pthread_mutex_unlock(&done_mutex);
+}
+
static void hotword_models_cb(struct cras_client *client,
const char *hotword_models)
{
@@ -1446,7 +1587,7 @@
cras_client_output_dev_plugged(client, name) ? "Yes" : "No");
}
-/* Repeatedly mute and unmute the output until there is an error. */
+/* Repeatedly mute and un-mute the output until there is an error. */
static void mute_loop_test(struct cras_client *client, int auto_reconnect)
{
int mute = 0;
@@ -1587,6 +1728,7 @@
{"connection_type", required_argument, 0, 'K'},
{"loopback_file", required_argument, 0, 'L'},
{"mute_loop_test", required_argument, 0, 'M'},
+ {"dump_main", no_argument, 0, 'N'},
{"playback_file", required_argument, 0, 'P'},
{"stream_type", required_argument, 0, 'T'},
{0, 0, 0, 0}
@@ -1610,7 +1752,7 @@
printf("--capture_file <name> - "
"Name of file to record to.\n");
printf("--capture_gain <dB> - "
- "Set system caputre gain in dB*100 (100 = 1dB).\n");
+ "Set system capture gain in dB*100 (100 = 1dB).\n");
printf("--capture_mute <0|1> - "
"Set capture mute state.\n");
printf("--channel_layout <layout_str> - "
@@ -1633,6 +1775,8 @@
"Dumps audio thread info.\n");
printf("--dump_bt - "
"Dumps debug info for bt audio\n");
+ printf("--dump_main - "
+ "Dumps debug info from main thread\n");
printf("--dump_dsp - "
"Print status of dsp to syslog.\n");
printf("--dump_server_info - "
@@ -1657,7 +1801,7 @@
printf("--mute <0|1> - "
"Set system mute state.\n");
printf("--mute_loop_test <0|1> - "
- "Continuously loop mute/umute.\n"
+ "Continuously loop mute/un-mute.\n"
" "
"Argument: 0 - stop on error.\n"
" "
@@ -1711,7 +1855,7 @@
printf("--suspend <0|1> - "
"Set audio suspend state.\n");
printf("--swap_left_right <N>:<M>:<0|1> - "
- "Swap or unswap (1 or 0) the left and right channel for the "
+ "Swap or un-swap (1 or 0) the left and right channel for the "
"ionode with the given index M on the device with index N\n");
printf("--stream_type <N> - "
"Specify the type of the stream.\n");
@@ -2030,7 +2174,8 @@
s = strtok(optarg, ":");
nch = atoi(s);
- coeff = (float *)calloc(nch * nch, sizeof(*coeff));
+ coeff = (float *)calloc((size_t)nch * (size_t)nch,
+ sizeof(*coeff));
for (size = 0; size < nch * nch; size++) {
s = strtok(NULL, ",");
if (NULL == s)
@@ -2151,6 +2296,9 @@
case 'M':
mute_loop_test(client, atoi(optarg));
break;
+ case 'N':
+ show_main_thread_debug_info(client);
+ break;
case 'P':
playback_file = optarg;
break;
diff --git a/scripts/audio_diagnostics b/scripts/audio_diagnostics
index e6818d7..b754c07 100755
--- a/scripts/audio_diagnostics
+++ b/scripts/audio_diagnostics
@@ -7,7 +7,8 @@
# Collect information about the audio system from top to bottom.
dump_cards() {
- for card in "${@}"
+ # shellcheck disable=SC2068
+ for card in ${@}
do
echo "=== amixer -c ${card} scontents ==="
amixer -c "${card}" scontents
@@ -34,6 +35,9 @@
echo '=== cras_test_client --dump_audio_thread ==='
cras_test_client --dump_audio_thread
+echo '=== cras_test_client --dump_main ==='
+cras_test_client --dump_main
+
echo '=== cras_test_client --dump_bt ==='
cras_test_client --dump_bt
diff --git a/sound_card_init/max98390d/src/amp_calibration.rs b/sound_card_init/max98390d/src/amp_calibration.rs
index 5ee96bb..50f4227 100644
--- a/sound_card_init/max98390d/src/amp_calibration.rs
+++ b/sound_card_init/max98390d/src/amp_calibration.rs
@@ -25,10 +25,11 @@
const CALI_ERROR_LOWER_LIMIT: f32 = 0.03;
const FRAMES_PER_BUFFER: usize = 256;
-const FRAME_RATE: usize = 48000;
+const FRAME_RATE: u32 = 48000;
const NUM_CHANNELS: usize = 2;
const FORMAT: SampleFormat = SampleFormat::S16LE;
-const DURATION_MS: usize = 1000;
+const DURATION_MS: u32 = 1000;
+const WARM_UP_DURATION_MS: u32 = 300;
/// Amp volume mode emulation used by set_volume().
#[derive(PartialEq)]
@@ -267,7 +268,9 @@
let handle = thread::spawn(move || -> Result<()> {
let local_buffer = [0u8; FRAMES_PER_BUFFER * NUM_CHANNELS * 2];
- let iterations: usize = (FRAME_RATE * DURATION_MS) / FRAMES_PER_BUFFER / 1000;
+ let iterations = (FRAME_RATE * DURATION_MS) / FRAMES_PER_BUFFER as u32 / 1000;
+ let warm_up_iterations =
+ (FRAME_RATE * WARM_UP_DURATION_MS) / FRAMES_PER_BUFFER as u32 / 1000;
let (_control, mut stream) = cras_client
.new_pinned_playback_stream(
@@ -290,7 +293,8 @@
let _write_frames = buffer.write(&local_buffer).map_err(Error::PlaybackFailed)?;
// Notifies the main thread to start the calibration.
- if i == 1 {
+ // The mute playing time need to be longer than WARM_UP_DURATION_MS to get rdc properly.
+ if i == warm_up_iterations {
let (lock, cvar) = &*playback_started;
let mut started = lock.lock()?;
*started = true;
diff --git a/sound_card_init/max98390d/src/error.rs b/sound_card_init/max98390d/src/error.rs
index 6381180..572340d 100644
--- a/sound_card_init/max98390d/src/error.rs
+++ b/sound_card_init/max98390d/src/error.rs
@@ -25,6 +25,7 @@
InvalidDatastore,
InvalidTemperature(i32),
LargeCalibrationDiff(i32, i32),
+ MissingDSMParam,
MutexPoisonError,
NewPlayStreamFailed(libcras::BoxError),
NextPlaybackBufferFailed(libcras::BoxError),
@@ -79,6 +80,7 @@
rdc, temp
),
MutexPoisonError => write!(f, "mutex is poisoned"),
+ MissingDSMParam => write!(f, "missing dsm_param.bin"),
NewPlayStreamFailed(e) => write!(f, "{}", e),
NextPlaybackBufferFailed(e) => write!(f, "{}", e),
PlaybackFailed(e) => write!(f, "{}", e),
diff --git a/sound_card_init/max98390d/src/lib.rs b/sound_card_init/max98390d/src/lib.rs
index c9f63f5..7e445c5 100644
--- a/sound_card_init/max98390d/src/lib.rs
+++ b/sound_card_init/max98390d/src/lib.rs
@@ -10,6 +10,8 @@
mod settings;
mod vpd;
+use std::path::Path;
+
use cros_alsa::Card;
use sys_util::error;
@@ -32,6 +34,21 @@
let settings = DeviceSettings::from_yaml_str(conf)?;
let mut card = Card::new(snd_card)?;
+ if !Path::new(&settings.dsm_param).exists() {
+ for s in &settings.amp_calibrations {
+ let mut amp_calib = match AmpCalibration::new(&mut card, s.clone()) {
+ Ok(amp) => amp,
+ Err(e) => {
+ error!("{}.", e);
+ continue;
+ }
+ };
+ if let Err(e) = amp_calib.set_volume(VolumeMode::Low) {
+ error!("failed to set volume to low: {}.", e);
+ }
+ }
+ return Err(Error::MissingDSMParam);
+ }
// If some error occurs during the calibration, the iteration will continue running the
// calibration for the next amp.
let results: Vec<Result<()>> = settings
diff --git a/sound_card_init/max98390d/src/settings.rs b/sound_card_init/max98390d/src/settings.rs
index 5e7ffe3..32feefd 100644
--- a/sound_card_init/max98390d/src/settings.rs
+++ b/sound_card_init/max98390d/src/settings.rs
@@ -7,10 +7,13 @@
use crate::error::{Error, Result};
-/// `DeviceSettings` includes the settings of max98390. It currently includes the settings of amplifier calibration.
+/// `DeviceSettings` includes the settings of max98390. It currently includes:
+/// * the settings of amplifier calibration.
+/// * the path of dsm_param.
#[derive(Debug, Default, PartialEq, Deserialize, Clone)]
pub struct DeviceSettings {
pub amp_calibrations: Vec<AmpCalibSettings>,
+ pub dsm_param: String,
}
/// `AmpCalibSettings` includes the settings needed for amplifier calibration.
diff --git a/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy b/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy
index 5bbdb73..bddf6ea 100644
--- a/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy
+++ b/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy
@@ -75,3 +75,4 @@
rt_sigreturn: 1
wait4: 1
restart_syscall: 1
+sched_yield: 1
\ No newline at end of file
diff --git a/sound_card_init/sound_card_init.conf b/sound_card_init/sound_card_init.conf
index 0f504fa..7ad1a80 100644
--- a/sound_card_init/sound_card_init.conf
+++ b/sound_card_init/sound_card_init.conf
@@ -26,8 +26,8 @@
"Invalid SOUND_CARD_ID supplied"
exit 1
else
- mkdir -p /var/lib/sound_card_init/"${SOUND_CARD_ID}"
- chown sound_card_init:sound_card_init /var/lib/sound_card_init/"${SOUND_CARD_ID}"
+ mkdir -m 0755 -p /var/lib/sound_card_init/"${SOUND_CARD_ID}"
+ chown -R sound_card_init:sound_card_init /var/lib/sound_card_init
fi
end script
diff --git a/ucm-config/for_all_boards/ICUSBAUDIO7D/HiFi.conf b/ucm-config/for_all_boards/ICUSBAUDIO7D/HiFi.conf
new file mode 100644
index 0000000..c44de8d
--- /dev/null
+++ b/ucm-config/for_all_boards/ICUSBAUDIO7D/HiFi.conf
@@ -0,0 +1,132 @@
+SectionVerb {
+ Value {
+ FullySpecifiedUCM "1"
+ }
+
+ EnableSequence [
+ cdev "hw:ICUSBAUDIO7D"
+
+ cset "name='Line Capture Switch', off"
+ cset "name='Mic Capture Switch', off"
+ cset "name='IEC958 In Capture Switch', off"
+ cset "name='PCM Capture Switch', off"
+ cset "name='Speaker Playback Switch', off"
+ cset "name='Mic Playback Switch', off"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionDevice."Speaker-Headset".0 {
+ Comment "Speaker out"
+
+ Value {
+ PlaybackPCM "hw:ICUSBAUDIO7D,0"
+ PlaybackMixerElem "Speaker"
+ }
+
+ EnableSequence [
+ cset "name='Speaker Playback Switch', on"
+ ]
+
+ DisableSequence [
+ cset "name='Speaker Playback Switch', off"
+ ]
+}
+
+SectionDevice."Line In".0 {
+ Comment "Line In"
+
+ Value {
+ CapturePCM "hw:ICUSBAUDIO7D,0"
+ CaptureMixerElem "Line"
+ }
+
+ ConflictingDevice [
+ "Mic"
+ "SPDIF In"
+ "PCM"
+ ]
+
+ EnableSequence [
+ cset "name='Line Capture Switch', on"
+ cset "name='PCM Capture Source', Line"
+ ]
+
+ DisableSequence [
+ cset "name='Line Capture Switch', off"
+ ]
+}
+
+SectionDevice."Mic".0 {
+ Comment "Mic Input"
+
+ Value {
+ CapturePCM "hw:ICUSBAUDIO7D,0"
+ CaptureMixerElem "Mic"
+ }
+
+ ConflictingDevice [
+ "Line In"
+ "SPDIF In"
+ "PCM"
+ ]
+
+ EnableSequence [
+ cset "name='Mic Capture Switch', on"
+ cset "name='PCM Capture Source', Mic"
+ ]
+
+ DisableSequence [
+ cset "name='Mic Capture Switch', off"
+ ]
+}
+
+SectionDevice."SPDIF In".0 {
+ Comment "S/PDIF In"
+
+ Value {
+ CapturePCM "hw:ICUSBAUDIO7D,0"
+ CaptureMixerElem "IEC958 In"
+ }
+
+ ConflictingDevice [
+ "Line In"
+ "Mic"
+ "PCM"
+ ]
+
+ EnableSequence [
+ cset "name='IEC958 In Capture Switch', on"
+ cset "name='PCM Capture Source', IEC958 In"
+ ]
+
+ DisableSequence [
+ cset "name='IEC958 In Capture Switch', off"
+ ]
+}
+
+SectionDevice."PCM".0 {
+ Comment "PCM Capture"
+
+ Value {
+ CapturePCM "hw:ICUSBAUDIO7D,0"
+ CaptureMixerElem "PCM"
+ }
+
+ ConflictingDevice [
+ "Line In"
+ "Mic"
+ "SPDIF In"
+ ]
+
+ EnableSequence [
+ cset "name='PCM Capture Switch', on"
+ cset "name='PCM Capture Source', Mixer"
+ ]
+
+ DisableSequence [
+ cset "name='PCM Capture Switch', off"
+ ]
+}
diff --git a/ucm-config/for_all_boards/ICUSBAUDIO7D/ICUSBAUDIO7D.conf b/ucm-config/for_all_boards/ICUSBAUDIO7D/ICUSBAUDIO7D.conf
new file mode 100644
index 0000000..3dc10fa
--- /dev/null
+++ b/ucm-config/for_all_boards/ICUSBAUDIO7D/ICUSBAUDIO7D.conf
@@ -0,0 +1,6 @@
+Comment "Startech USB 7D Audio"
+
+SectionUseCase."HiFi" {
+ File "HiFi.conf"
+ Comment "Default"
+}