summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDylan Reid <dgreid@chromium.org>2017-06-07 09:50:59 -0700
committerDylan Reid <dgreid@google.com>2017-06-07 09:53:49 -0700
commitd4592bd501f5898d7f00e709711d896e04a88c40 (patch)
tree022092b89799875b28fcd9457b63521a75b914e3
parent501f4deaee648493ff2b6e7e22df1f1f8d4c1517 (diff)
parent81b3032369be1bdb5976a1f7772b6c9fb94229ac (diff)
downloadadhd-d4592bd501f5898d7f00e709711d896e04a88c40.tar.gz
Merge remote-tracking branch 'cros/master' into HEAD
Nothing currently builds the master branch here. Update it for use on Bertha. Change-Id: I9e3e2c4e2a50383e810b21608b61f72c3027c72c Signed-off-by: Dylan Reid <dgreid@chromium.org>
-rw-r--r--.gitignore1
-rw-r--r--Makefile40
-rw-r--r--OWNERS6
-rw-r--r--cras-config/banjo/byt-max98090107
-rw-r--r--cras-config/banjo/dsp.ini111
-rw-r--r--cras-config/candy/byt-max98090107
-rw-r--r--cras-config/candy/dsp.ini87
-rw-r--r--cras-config/clapper/byt-max98090107
-rw-r--r--cras-config/clapper/dsp.ini103
l---------cras-config/cyan-cheets1
-rw-r--r--cras-config/enguarde/byt-max98090107
-rw-r--r--cras-config/enguarde/dsp.ini103
-rw-r--r--cras-config/expresso/byt-max98090107
-rw-r--r--cras-config/expresso/dsp.ini103
-rw-r--r--cras-config/glimmer-cheets/byt-max98090 (renamed from cras-config/glimmer/byt-max98090)0
-rw-r--r--cras-config/glimmer-cheets/dsp.ini (renamed from cras-config/glimmer/dsp.ini)0
-rw-r--r--cras-config/gnawty/OLAY/byt-max98090107
-rw-r--r--cras-config/gnawty/OLAY/dsp.ini111
-rw-r--r--cras-config/gnawty/byt-max98090107
-rw-r--r--cras-config/gnawty/dsp.ini103
-rwxr-xr-xcras-config/gnawty/get_device_config_dir12
-rw-r--r--cras-config/heli/byt-max98090107
-rw-r--r--cras-config/heli/dsp.ini111
-rw-r--r--cras-config/kip/KIP14/byt-max98090107
-rw-r--r--cras-config/kip/KIP14/dsp.ini119
-rw-r--r--cras-config/kip/dsp.ini111
-rwxr-xr-xcras-config/kip/get_device_config_dir12
-rw-r--r--cras-config/orco/byt-max98090107
-rw-r--r--cras-config/orco/dsp.ini62
-rw-r--r--cras-config/quawks/byt-max98090206
-rw-r--r--cras-config/quawks/dsp.ini103
-rw-r--r--cras-config/squawks/byt-max98090206
-rw-r--r--cras-config/squawks/dsp.ini103
-rw-r--r--cras-config/winky/byt-max98090206
-rw-r--r--cras-config/winky/dsp.ini111
-rw-r--r--cras/.gitignore9
-rw-r--r--cras/README14
-rw-r--r--cras/README.dbus-api32
-rw-r--r--cras/configure.ac96
-rw-r--r--cras/src/Android.mk6
-rw-r--r--cras/src/Makefile.am287
-rw-r--r--cras/src/arm/shm.S55
-rw-r--r--cras/src/common/byte_buffer.h (renamed from cras/src/server/byte_buffer.h)0
-rw-r--r--cras/src/common/cras_audio_format.h32
-rw-r--r--cras/src/common/cras_config.c2
-rw-r--r--cras/src/common/cras_config.h4
-rw-r--r--cras/src/common/cras_file_wait.c315
-rw-r--r--cras/src/common/cras_file_wait.h129
-rw-r--r--cras/src/common/cras_iodev_info.h11
-rw-r--r--cras/src/common/cras_messages.h243
-rw-r--r--cras/src/common/cras_observer_ops.h54
-rw-r--r--cras/src/common/cras_shm.c98
-rw-r--r--cras/src/common/cras_shm.h61
-rw-r--r--cras/src/common/cras_types.h73
-rw-r--r--cras/src/common/cras_util.c160
-rw-r--r--cras/src/common/cras_util.h59
-rw-r--r--cras/src/common/edid_utils.c4
-rw-r--r--cras/src/dsp/drc.c96
-rw-r--r--cras/src/dsp/drc_kernel.c68
-rw-r--r--cras/src/dsp/drc_math.h19
-rw-r--r--cras/src/dsp/dsp_util.c226
-rw-r--r--cras/src/dsp/tests/dsp_util_test.c376
-rw-r--r--cras/src/libcras/cras_client.c1961
-rw-r--r--cras/src/libcras/cras_client.h598
-rw-r--r--cras/src/libcras/cras_helpers.c29
-rw-r--r--cras/src/libcras/cras_helpers.h22
-rw-r--r--cras/src/server/audio_thread.c548
-rw-r--r--cras/src/server/audio_thread.h34
-rw-r--r--cras/src/server/config/cras_card_config.c7
-rw-r--r--cras/src/server/config/cras_card_config.h3
-rw-r--r--cras/src/server/cras.c32
-rw-r--r--cras/src/server/cras_a2dp_endpoint.c118
-rw-r--r--cras/src/server/cras_a2dp_endpoint.h7
-rw-r--r--cras/src/server/cras_a2dp_info.c7
-rw-r--r--cras/src/server/cras_a2dp_info.h2
-rw-r--r--cras/src/server/cras_a2dp_iodev.c132
-rw-r--r--cras/src/server/cras_a2dp_iodev.h9
-rw-r--r--cras/src/server/cras_alert.c65
-rw-r--r--cras/src/server/cras_alert.h34
-rw-r--r--cras/src/server/cras_alsa_card.c507
-rw-r--r--cras/src/server/cras_alsa_card.h4
-rw-r--r--cras/src/server/cras_alsa_helpers.c200
-rw-r--r--cras/src/server/cras_alsa_helpers.h81
-rw-r--r--cras/src/server/cras_alsa_io.c1436
-rw-r--r--cras/src/server/cras_alsa_io.h63
-rw-r--r--cras/src/server/cras_alsa_jack.c743
-rw-r--r--cras/src/server/cras_alsa_jack.h46
-rw-r--r--cras/src/server/cras_alsa_mixer.c1100
-rw-r--r--cras/src/server/cras_alsa_mixer.h101
-rw-r--r--cras/src/server/cras_alsa_mixer_name.c134
-rw-r--r--cras/src/server/cras_alsa_mixer_name.h104
-rw-r--r--cras/src/server/cras_alsa_ucm.c803
-rw-r--r--cras/src/server/cras_alsa_ucm.h317
-rw-r--r--cras/src/server/cras_alsa_ucm_section.c129
-rw-r--r--cras/src/server/cras_alsa_ucm_section.h105
-rw-r--r--cras/src/server/cras_audio_area.c15
-rw-r--r--cras/src/server/cras_audio_area.h4
-rw-r--r--cras/src/server/cras_bt_adapter.c4
-rw-r--r--cras/src/server/cras_bt_constants.h2
-rw-r--r--cras/src/server/cras_bt_device.c496
-rw-r--r--cras/src/server/cras_bt_device.h68
-rw-r--r--cras/src/server/cras_bt_endpoint.c13
-rw-r--r--cras/src/server/cras_bt_endpoint.h4
-rw-r--r--cras/src/server/cras_bt_io.c62
-rw-r--r--cras/src/server/cras_bt_manager.c4
-rw-r--r--cras/src/server/cras_bt_player.c179
-rw-r--r--cras/src/server/cras_bt_player.h49
-rw-r--r--cras/src/server/cras_bt_profile.c2
-rw-r--r--cras/src/server/cras_bt_transport.c200
-rw-r--r--cras/src/server/cras_bt_transport.h33
-rw-r--r--cras/src/server/cras_dbus_control.c347
-rw-r--r--cras/src/server/cras_dbus_util.c5
-rw-r--r--cras/src/server/cras_dbus_util.h8
-rw-r--r--cras/src/server/cras_device_monitor.c101
-rw-r--r--cras/src/server/cras_device_monitor.h20
-rw-r--r--cras/src/server/cras_dsp.c14
-rw-r--r--cras/src/server/cras_dsp.h23
-rw-r--r--cras/src/server/cras_dsp_ini.c127
-rw-r--r--cras/src/server/cras_dsp_mod_builtin.c52
-rw-r--r--cras/src/server/cras_dsp_pipeline.c2
-rw-r--r--cras/src/server/cras_empty_iodev.c42
-rw-r--r--cras/src/server/cras_fmt_conv.c52
-rw-r--r--cras/src/server/cras_fmt_conv.h23
-rw-r--r--cras/src/server/cras_gpio_jack.c67
-rw-r--r--cras/src/server/cras_gpio_jack.h32
-rw-r--r--cras/src/server/cras_hfp_ag_profile.c109
-rw-r--r--cras/src/server/cras_hfp_ag_profile.h6
-rw-r--r--cras/src/server/cras_hfp_info.c5
-rw-r--r--cras/src/server/cras_hfp_info.h3
-rw-r--r--cras/src/server/cras_hfp_iodev.c36
-rw-r--r--cras/src/server/cras_hfp_iodev.h2
-rw-r--r--cras/src/server/cras_hfp_slc.c16
-rw-r--r--cras/src/server/cras_hfp_slc.h5
-rw-r--r--cras/src/server/cras_iodev.c636
-rw-r--r--cras/src/server/cras_iodev.h309
-rw-r--r--cras/src/server/cras_iodev_list.c515
-rw-r--r--cras/src/server/cras_iodev_list.h53
-rw-r--r--cras/src/server/cras_loopback_iodev.c38
-rw-r--r--cras/src/server/cras_main_message.c2
-rw-r--r--cras/src/server/cras_main_message.h3
-rw-r--r--cras/src/server/cras_mix.c590
-rw-r--r--cras/src/server/cras_mix.h27
-rw-r--r--cras/src/server/cras_mix_ops.c862
-rw-r--r--cras/src/server/cras_mix_ops.h49
-rw-r--r--cras/src/server/cras_observer.c507
-rw-r--r--cras/src/server/cras_observer.h97
-rw-r--r--cras/src/server/cras_ramp.c150
-rw-r--r--cras/src/server/cras_ramp.h74
-rw-r--r--cras/src/server/cras_rclient.c269
-rw-r--r--cras/src/server/cras_rclient.h6
-rw-r--r--cras/src/server/cras_rstream.c89
-rw-r--r--cras/src/server/cras_rstream.h21
-rw-r--r--cras/src/server/cras_server.c113
-rw-r--r--cras/src/server/cras_server.h9
-rw-r--r--cras/src/server/cras_server_metrics.c25
-rw-r--r--cras/src/server/cras_system_state.c234
-rw-r--r--cras/src/server/cras_system_state.h107
-rw-r--r--cras/src/server/cras_tm.c15
-rw-r--r--cras/src/server/cras_udev.c28
-rw-r--r--cras/src/server/cras_utf8.c198
-rw-r--r--cras/src/server/cras_utf8.h36
-rw-r--r--cras/src/server/dev_stream.c207
-rw-r--r--cras/src/server/dev_stream.h27
-rw-r--r--cras/src/server/linear_resampler.c2
-rw-r--r--cras/src/server/softvol_curve.c17
-rw-r--r--cras/src/server/test_iodev.c35
-rw-r--r--cras/src/tests/a2dp_iodev_unittest.cc106
-rw-r--r--cras/src/tests/alert_unittest.cc85
-rw-r--r--cras/src/tests/alsa_card_unittest.cc657
-rw-r--r--cras/src/tests/alsa_helpers_unittest.cc175
-rw-r--r--cras/src/tests/alsa_io_unittest.cc1862
-rw-r--r--cras/src/tests/alsa_jack_unittest.cc620
-rw-r--r--cras/src/tests/alsa_mixer_unittest.cc886
-rw-r--r--cras/src/tests/alsa_ucm_unittest.cc1022
-rw-r--r--cras/src/tests/audio_area_unittest.cc86
-rw-r--r--cras/src/tests/audio_test_gui.py266
-rw-r--r--cras/src/tests/audio_thread_unittest.cc488
-rw-r--r--cras/src/tests/bt_device_unittest.cc63
-rw-r--r--cras/src/tests/bt_io_unittest.cc66
-rw-r--r--cras/src/tests/bt_profile_unittest.cc10
-rw-r--r--cras/src/tests/card_config_unittest.cc22
-rw-r--r--cras/src/tests/cras_client_unittest.cc160
-rw-r--r--cras/src/tests/cras_dsp_pipeline_unittest.cc1
-rw-r--r--cras/src/tests/cras_monitor.c319
-rw-r--r--cras/src/tests/cras_router.c269
-rw-r--r--cras/src/tests/cras_test_client.c395
-rw-r--r--cras/src/tests/dev_stream_unittest.cc354
-rw-r--r--cras/src/tests/device_blacklist_unittest.cc2
-rw-r--r--cras/src/tests/device_monitor_unittest.cc154
-rw-r--r--cras/src/tests/dsp_ini_unittest.cc127
-rw-r--r--cras/src/tests/dsp_unittest.cc10
-rw-r--r--cras/src/tests/file_wait_unittest.cc279
-rw-r--r--cras/src/tests/fmt_conv_unittest.cc33
-rw-r--r--cras/src/tests/hfp_info_unittest.cc11
-rw-r--r--cras/src/tests/hfp_iodev_unittest.cc34
-rw-r--r--cras/src/tests/hfp_slc_unittest.cc28
-rw-r--r--cras/src/tests/iodev_list_unittest.cc908
-rw-r--r--cras/src/tests/iodev_unittest.cc1267
-rw-r--r--cras/src/tests/linear_resampler_unittest.cc6
-rw-r--r--cras/src/tests/loopback_iodev_unittest.cc17
-rw-r--r--cras/src/tests/mix_unittest.cc597
-rw-r--r--cras/src/tests/observer_unittest.cc625
-rw-r--r--cras/src/tests/ramp_unittest.cc406
-rw-r--r--cras/src/tests/rclient_unittest.cc409
-rw-r--r--cras/src/tests/rstream_unittest.cc72
-rw-r--r--cras/src/tests/server_metrics_unittest.cc72
-rw-r--r--cras/src/tests/shm_unittest.cc30
-rw-r--r--cras/src/tests/stream_list_unittest.cc3
-rw-r--r--cras/src/tests/system_state_unittest.cc307
-rw-r--r--cras/src/tests/utf8_unittest.cc154
-rw-r--r--cras/src/tests/util_unittest.cc181
-rw-r--r--defs/utilities.mk1
-rw-r--r--init/cras-directories.conf1
-rw-r--r--init/cras.conf25
-rw-r--r--init/cras.service14
-rw-r--r--init/cras.sh25
-rwxr-xr-xscripts/audio_diagnostics23
-rw-r--r--scripts/audio_thread_log_viewer/log.test6151
-rwxr-xr-xscripts/audio_thread_log_viewer/viewer_c3.py567
-rw-r--r--scripts/audio_tuning/frontend/audio.js83
-rw-r--r--ucm-config/banjo/byt-max98090/HiFi.conf97
-rw-r--r--ucm-config/candy/byt-max98090/HiFi.conf97
-rw-r--r--ucm-config/candy/byt-max98090/byt-max98090.conf6
l---------ucm-config/chell1
l---------ucm-config/chell-cheets1
-rw-r--r--ucm-config/clapper/byt-max98090/HiFi.conf119
-rw-r--r--ucm-config/clapper/byt-max98090/byt-max98090.conf6
-rw-r--r--ucm-config/cranky/byt-max98090/HiFi.conf97
-rw-r--r--ucm-config/cranky/byt-max98090/byt-max98090.conf6
l---------ucm-config/cyan-cheets1
-rw-r--r--ucm-config/cyan/chtmax98090/HiFi.conf22
-rw-r--r--ucm-config/daisy/DAISY-I2S/HiFi.conf1
-rw-r--r--ucm-config/daisy_skate/DAISY-I2S/HiFi.conf4
-rw-r--r--ucm-config/daisy_spring/DAISY-I2S/HiFi.conf6
-rw-r--r--ucm-config/enguarde/byt-max98090/byt-max98090.conf6
-rw-r--r--ucm-config/expresso/byt-max98090/HiFi.conf97
-rw-r--r--ucm-config/expresso/byt-max98090/byt-max98090.conf6
-rw-r--r--ucm-config/glados/sklnau8825adi/HiFi.conf507
-rw-r--r--ucm-config/glados/sklnau8825adi/sklnau8825adi.conf (renamed from ucm-config/banjo/byt-max98090/byt-max98090.conf)2
-rw-r--r--ucm-config/glimmer-cheets/byt-max98090/HiFi.conf (renamed from ucm-config/glimmer/byt-max98090/HiFi.conf)0
-rw-r--r--ucm-config/glimmer-cheets/byt-max98090/byt-max98090.conf (renamed from ucm-config/glimmer/byt-max98090/byt-max98090.conf)0
-rw-r--r--ucm-config/gnawty/byt-max98090/HiFi.conf97
-rw-r--r--ucm-config/gnawty/byt-max98090/byt-max98090.conf6
-rw-r--r--ucm-config/heli/byt-max98090/HiFi.conf97
-rw-r--r--ucm-config/heli/byt-max98090/byt-max98090.conf6
-rw-r--r--ucm-config/kip/byt-max98090/HiFi.conf97
-rw-r--r--ucm-config/ninja/byt-max98090/HiFi.conf81
-rw-r--r--ucm-config/ninja/byt-max98090/byt-max98090.conf6
-rw-r--r--ucm-config/orco/byt-max98090/HiFi.conf97
-rw-r--r--ucm-config/parry/byt-max98090/HiFi.conf97
-rw-r--r--ucm-config/parry/byt-max98090/byt-max98090.conf6
-rw-r--r--ucm-config/quawks/byt-max98090/HiFi.conf97
-rw-r--r--ucm-config/quawks/byt-max98090/byt-max98090.conf6
-rw-r--r--ucm-config/rambi/byt-max98090/HiFi.conf97
-rw-r--r--ucm-config/rambi/byt-max98090/byt-max98090.conf6
-rw-r--r--ucm-config/squawks/byt-max98090/HiFi.conf97
-rw-r--r--ucm-config/squawks/byt-max98090/byt-max98090.conf6
-rw-r--r--ucm-config/sumo/byt-max98090/HiFi.conf97
-rw-r--r--ucm-config/sumo/byt-max98090/byt-max98090.conf6
-rw-r--r--ucm-config/veyron/ROCKCHIP-I2S/HiFi.conf43
-rw-r--r--ucm-config/veyron/RockchipHDMI/HiFi.conf1
-rw-r--r--ucm-config/veyron_fievel/ROCKCHIP-I2S/HiFi.conf (renamed from ucm-config/enguarde/byt-max98090/HiFi.conf)56
-rw-r--r--ucm-config/veyron_fievel/ROCKCHIP-I2S/ROCKCHIP-I2S.conf (renamed from ucm-config/kip/byt-max98090/byt-max98090.conf)2
-rw-r--r--ucm-config/veyron_fievel/RockchipHDMI/HiFi.conf14
-rw-r--r--ucm-config/veyron_fievel/RockchipHDMI/RockchipHDMI.conf (renamed from ucm-config/orco/byt-max98090/byt-max98090.conf)2
-rw-r--r--ucm-config/veyron_jaq/ROCKCHIP-I2S/HiFi.conf43
-rw-r--r--ucm-config/veyron_jaq/RockchipHDMI/HiFi.conf1
-rw-r--r--ucm-config/veyron_minnie/ROCKCHIP-I2S/HiFi.conf65
-rw-r--r--ucm-config/veyron_minnie/RockchipHDMI/HiFi.conf1
-rw-r--r--ucm-config/veyron_speedy/ROCKCHIP-I2S/HiFi.conf43
-rw-r--r--ucm-config/veyron_speedy/RockchipHDMI/HiFi.conf1
-rw-r--r--ucm-config/winky/byt-max98090/HiFi.conf97
-rw-r--r--ucm-config/winky/byt-max98090/byt-max98090.conf6
-rw-r--r--upstart/cras.conf57
274 files changed, 35351 insertions, 10492 deletions
diff --git a/.gitignore b/.gitignore
index 98c5c08b..2854f052 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
build
cscope.*
+PRESUBMIT.cfg
diff --git a/Makefile b/Makefile
index 33b631b3..2be9ec9b 100644
--- a/Makefile
+++ b/Makefile
@@ -19,9 +19,36 @@ cras-scripts:
$(INSTALL) --mode 755 -D $(ADHD_DIR)/scripts/audio_diagnostics \
$(DESTDIR)usr/bin/
-$(DESTDIR)/etc/init/cras.conf: $(ADHD_DIR)/upstart/cras.conf
- $(ECHO) "Installing '$<' to '$@'"
- $(INSTALL) --mode 644 -D $< $@
+cras_init_upstart: $(ADHD_DIR)/init/cras.conf
+ $(ECHO) "Installing upstart file"
+ $(INSTALL) --mode 644 -D $< $(DESTDIR)/etc/init/cras.conf
+
+cras_init_scripts: $(ADHD_DIR)/init/cras.sh
+ $(INSTALL) --mode 644 -D $< $(DESTDIR)/usr/share/cros/init/cras.sh
+
+SYSTEMD_UNIT_DIR := /usr/lib/systemd/system/
+SYSTEMD_TMPFILESD_DIR := /usr/lib/tmpfiles.d/
+
+cras_init_systemd: $(ADHD_DIR)/init/cras.service \
+ $(ADHD_DIR)/init/cras-directories.conf
+ $(ECHO) "Installing systemd files"
+ $(INSTALL) --mode 644 -D $(ADHD_DIR)/init/cras.service \
+ $(DESTDIR)/$(SYSTEMD_UNIT_DIR)/cras.service
+ $(INSTALL) --mode 755 -d $(DESTDIR)/$(SYSTEMD_UNIT_DIR)/system-services.target.wants
+ $(LINK) -s ../cras.service \
+ $(DESTDIR)/$(SYSTEMD_UNIT_DIR)/system-services.target.wants/cras.service
+ $(INSTALL) --mode 644 -D $(ADHD_DIR)/init/cras-directories.conf \
+ $(DESTDIR)/$(SYSTEMD_TMPFILESD_DIR)/cras-directories.conf
+
+ifeq ($(strip $(SYSTEMD)), yes)
+
+cras_init: cras_init_systemd cras_init_scripts
+
+else
+
+cras_init: cras_init_upstart cras_init_scripts
+
+endif
$(DESTDIR)/etc/cras/device_blacklist: $(ADHD_DIR)/cras-config/device_blacklist
$(ECHO) "Installing '$<' to '$@'"
@@ -51,10 +78,11 @@ install: $(DESTDIR)/lib/firmware/$(BOARD)_alsa.fw
endif
-install: $(DESTDIR)/etc/init/cras.conf \
- $(DESTDIR)/etc/cras/device_blacklist \
+install: $(DESTDIR)/etc/cras/device_blacklist \
cras-scripts \
- cras_install
+ cras_install \
+ cras_init
+
clean:
@rm -rf $(ADHD_BUILD_DIR)
diff --git a/OWNERS b/OWNERS
index b86edff2..85070ac5 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,2 +1,6 @@
dgreid@chromium.org
-thutt@chromium.org
+chinyue@chromium.org
+cychiang@chromium.org
+
+# For bluetooth support.
+hychao@chromium.org
diff --git a/cras-config/banjo/byt-max98090 b/cras-config/banjo/byt-max98090
deleted file mode 100644
index fba7d1b9..00000000
--- a/cras-config/banjo/byt-max98090
+++ /dev/null
@@ -1,107 +0,0 @@
-[Speaker]
- volume_curve = explicit
- db_at_100 = -200
- db_at_99 = -200
- db_at_98 = -250
- db_at_97 = -250
- db_at_96 = -300
- db_at_95 = -300
- db_at_94 = -350
- db_at_93 = -350
- db_at_92 = -400
- db_at_91 = -400
- db_at_90 = -400
- db_at_89 = -450
- db_at_88 = -450
- db_at_87 = -450
- db_at_86 = -450
- db_at_85 = -500
- db_at_84 = -500
- db_at_83 = -500
- db_at_82 = -550
- db_at_81 = -550
- db_at_80 = -600
- db_at_79 = -600
- db_at_78 = -650
- db_at_77 = -650
- db_at_76 = -700
- db_at_75 = -700
- db_at_74 = -750
- db_at_73 = -750
- db_at_72 = -800
- db_at_71 = -800
- db_at_70 = -850
- db_at_69 = -850
- db_at_68 = -900
- db_at_67 = -900
- db_at_66 = -950
- db_at_65 = -950
- db_at_64 = -1000
- db_at_63 = -1000
- db_at_62 = -1050
- db_at_61 = -1050
- db_at_60 = -1100
- db_at_59 = -1100
- db_at_58 = -1150
- db_at_57 = -1150
- db_at_56 = -1200
- db_at_55 = -1200
- db_at_54 = -1250
- db_at_53 = -1250
- db_at_52 = -1300
- db_at_51 = -1300
- db_at_50 = -1350
- db_at_49 = -1350
- db_at_48 = -1400
- db_at_47 = -1400
- db_at_46 = -1450
- db_at_45 = -1450
- db_at_44 = -1500
- db_at_43 = -1550
- db_at_42 = -1600
- db_at_41 = -1650
- db_at_40 = -1700
- db_at_39 = -1750
- db_at_38 = -1850
- db_at_37 = -1900
- db_at_36 = -2000
- db_at_35 = -2100
- db_at_34 = -2200
- db_at_33 = -2300
- db_at_32 = -2400
- db_at_31 = -2450
- db_at_30 = -2500
- db_at_29 = -2550
- db_at_28 = -2600
- db_at_27 = -2650
- db_at_26 = -2700
- db_at_25 = -2750
- db_at_24 = -2800
- db_at_23 = -2850
- db_at_22 = -2950
- db_at_21 = -3000
- db_at_20 = -3100
- db_at_19 = -3150
- db_at_18 = -3250
- db_at_17 = -3300
- db_at_16 = -3400
- db_at_15 = -3450
- db_at_14 = -3550
- db_at_13 = -3600
- db_at_12 = -3700
- db_at_11 = -3750
- db_at_10 = -3850
- db_at_9 = -3900
- db_at_8 = -4000
- db_at_7 = -4050
- db_at_6 = -4150
- db_at_5 = -4200
- db_at_4 = -4300
- db_at_3 = -4350
- db_at_2 = -4450
- db_at_1 = -4500
- db_at_0 = -4600
-[Headphone]
- volume_curve = simple_step
- volume_step = 70
- max_volume = 0
diff --git a/cras-config/banjo/dsp.ini b/cras-config/banjo/dsp.ini
deleted file mode 100644
index d226159b..00000000
--- a/cras-config/banjo/dsp.ini
+++ /dev/null
@@ -1,111 +0,0 @@
-[output_source]
-library=builtin
-label=source
-purpose=playback
-disable=(not (equal? dsp_name "speaker_eq"))
-output_0={src:0}
-output_1={src:1}
-
-[output_sink]
-library=builtin
-label=sink
-purpose=playback
-input_0={dst:0}
-input_1={dst:1}
-
-[drc]
-library=builtin
-label=drc
-input_0={src:0}
-input_1={src:1}
-output_2={intermediate:0}
-output_3={intermediate:1}
-input_4=1 ; emphasis_disabled
-input_5=0 ; f
-input_6=1 ; enable
-input_7=-24 ; threshold
-input_8=20 ; knee
-input_9=7.8 ; ratio
-input_10=0.003 ; attack
-input_11=0.915 ; release
-input_12=2 ; boost
-input_13=450 ; f
-input_14=1 ; enable
-input_15=-23 ; threshold
-input_16=24 ; knee
-input_17=11 ; ratio
-input_18=0.003 ; attack
-input_19=0.25 ; release
-input_20=1.5 ; boost
-input_21=2000 ; f
-input_22=1 ; enable
-input_23=-21 ; threshold
-input_24=25 ; knee
-input_25=12 ; ratio
-input_26=0.003 ; attack
-input_27=0.25 ; release
-input_28=0.7 ; boost
-
-[eq2]
-library=builtin
-label=eq2
-input_0={intermediate:0}
-input_1={intermediate:1}
-output_2={dst:0}
-output_3={dst:1}
-input_4=2 ; highpass
-input_5=180 ; freq
-input_6=0 ; Q
-input_7=0 ; gain
-input_8=2 ; highpass
-input_9=150 ; freq
-input_10=0 ; Q
-input_11=0 ; gain
-input_12=6 ; peaking
-input_13=200 ; freq
-input_14=2.8 ; Q
-input_15=-3.5 ; gain
-input_16=6 ; peaking
-input_17=200 ; freq
-input_18=2.8 ; Q
-input_19=-3.5 ; gain
-input_20=6 ; peaking
-input_21=500 ; freq
-input_22=2.5 ; Q
-input_23=-6 ; gain
-input_24=6 ; peaking
-input_25=500 ; freq
-input_26=2.5 ; Q
-input_27=-6 ; gain
-input_28=6 ; peaking
-input_29=3400 ; freq
-input_30=3.5 ; Q
-input_31=-5 ; gain
-input_32=6 ; peaking
-input_33=3400 ; freq
-input_34=3.5 ; Q
-input_35=-5 ; gain
-input_36=6 ; peaking
-input_37=800 ; freq
-input_38=3.5 ; Q
-input_39=-5 ; gain
-input_40=6 ; peaking
-input_41=800 ; freq
-input_42=3.5 ; Q
-input_43=-5 ; gain
-input_44=6 ; peaking
-input_45=5000 ; freq
-input_46=2 ; Q
-input_47=-7.8 ; gain
-input_48=5 ; highshelf
-input_49=5000 ; freq
-input_50=2 ; Q
-input_51=-7.8 ; gain
-input_52=6 ; peaking
-input_53=2000 ; freq
-input_54=4 ; Q
-input_55=-5 ; gain
-input_56=6 ; peaking
-input_57=2000 ; freq
-input_58=4 ; Q
-input_59=-5 ; gain
diff --git a/cras-config/candy/byt-max98090 b/cras-config/candy/byt-max98090
deleted file mode 100644
index 7e8b38b0..00000000
--- a/cras-config/candy/byt-max98090
+++ /dev/null
@@ -1,107 +0,0 @@
-[Speaker]
- volume_curve = explicit
- db_at_100 = 0
- db_at_99 = 0
- db_at_98 = -50
- db_at_97 = -50
- db_at_96 = -50
- db_at_95 = -100
- db_at_94 = -100
- db_at_93 = -150
- db_at_92 = -150
- db_at_91 = -150
- db_at_90 = -200
- db_at_89 = -200
- db_at_88 = -200
- db_at_87 = -250
- db_at_86 = -250
- db_at_85 = -300
- db_at_84 = -300
- db_at_83 = -350
- db_at_82 = -350
- db_at_81 = -400
- db_at_80 = -450
- db_at_79 = -500
- db_at_78 = -500
- db_at_77 = -550
- db_at_76 = -600
- db_at_75 = -650
- db_at_74 = -650
- db_at_73 = -700
- db_at_72 = -750
- db_at_71 = -800
- db_at_70 = -800
- db_at_69 = -850
- db_at_68 = -900
- db_at_67 = -950
- db_at_66 = -950
- db_at_65 = -1000
- db_at_64 = -1050
- db_at_63 = -1100
- db_at_62 = -1100
- db_at_61 = -1150
- db_at_60 = -1200
- db_at_59 = -1300
- db_at_58 = -1350
- db_at_57 = -1450
- db_at_56 = -1500
- db_at_55 = -1600
- db_at_54 = -1650
- db_at_53 = -1750
- db_at_52 = -1800
- db_at_51 = -1900
- db_at_50 = -1950
- db_at_49 = -2050
- db_at_48 = -2100
- db_at_47 = -2200
- db_at_46 = -2250
- db_at_45 = -2350
- db_at_44 = -2450
- db_at_43 = -2500
- db_at_42 = -2600
- db_at_41 = -2650
- db_at_40 = -2750
- db_at_39 = -2800
- db_at_38 = -2900
- db_at_37 = -2950
- db_at_36 = -3050
- db_at_35 = -3100
- db_at_34 = -3200
- db_at_33 = -3250
- db_at_32 = -3350
- db_at_31 = -3400
- db_at_30 = -3500
- db_at_29 = -3600
- db_at_28 = -3650
- db_at_27 = -3750
- db_at_26 = -3800
- db_at_25 = -3900
- db_at_24 = -3950
- db_at_23 = -4050
- db_at_22 = -4100
- db_at_21 = -4200
- db_at_20 = -4250
- db_at_19 = -4350
- db_at_18 = -4400
- db_at_17 = -4500
- db_at_16 = -4550
- db_at_15 = -4650
- db_at_14 = -4750
- db_at_13 = -4800
- db_at_12 = -4900
- db_at_11 = -4950
- db_at_10 = -5050
- db_at_9 = -5100
- db_at_8 = -5200
- db_at_7 = -5250
- db_at_6 = -5350
- db_at_5 = -5400
- db_at_4 = -5500
- db_at_3 = -5550
- db_at_2 = -5650
- db_at_1 = -5700
- db_at_0 = -5800
-[Headphone]
- volume_curve = simple_step
- volume_step = 70
- max_volume = 0
diff --git a/cras-config/candy/dsp.ini b/cras-config/candy/dsp.ini
deleted file mode 100644
index dc80534c..00000000
--- a/cras-config/candy/dsp.ini
+++ /dev/null
@@ -1,87 +0,0 @@
-[output_source]
-library=builtin
-label=source
-purpose=playback
-disable=(not (equal? dsp_name "speaker_eq"))
-output_0={src:0}
-output_1={src:1}
-
-[output_sink]
-library=builtin
-label=sink
-purpose=playback
-input_0={dst:0}
-input_1={dst:1}
-
-[drc]
-library=builtin
-label=drc
-input_0={src:0}
-input_1={src:1}
-output_2={intermediate:0}
-output_3={intermediate:1}
-input_4=1 ; emphasis_disabled
-input_5=0 ; f
-input_6=0 ; enable
-input_7=-24 ; threshold
-input_8=28 ; knee
-input_9=11.774 ; ratio
-input_10=0.003 ; attack
-input_11=0.25 ; release
-input_12=2 ; boost
-input_13=400 ; f
-input_14=1 ; enable
-input_15=-24 ; threshold
-input_16=29 ; knee
-input_17=16 ; ratio
-input_18=0.003 ; attack
-input_19=0.25 ; release
-input_20=0 ; boost
-input_21=2000 ; f
-input_22=0 ; enable
-input_23=-24 ; threshold
-input_24=30 ; knee
-input_25=12 ; ratio
-input_26=0.003 ; attack
-input_27=0.25 ; release
-input_28=0 ; boost
-
-[eq2]
-library=builtin
-label=eq2
-input_0={intermediate:0}
-input_1={intermediate:1}
-output_2={dst:0}
-output_3={dst:1}
-input_4=2 ; highpass
-input_5=300 ; freq
-input_6=0 ; Q
-input_7=0 ; gain
-input_8=2 ; highpass
-input_9=300 ; freq
-input_10=0 ; Q
-input_11=0 ; gain
-input_12=5 ; highshelf
-input_13=10000 ; freq
-input_14=0 ; Q
-input_15=-4 ; gain
-input_16=5 ; highshelf
-input_17=10000 ; freq
-input_18=0 ; Q
-input_19=-4 ; gain
-input_20=6 ; peaking
-input_21=600 ; freq
-input_22=2 ; Q
-input_23=-6 ; gain
-input_24=6 ; peaking
-input_25=600 ; freq
-input_26=2 ; Q
-input_27=-6 ; gain
-input_28=6 ; peaking
-input_29=1500 ; freq
-input_30=2 ; Q
-input_31=-4 ; gain
-input_32=6 ; peaking
-input_33=1500 ; freq
-input_34=2 ; Q
-input_35=-4 ; gain
diff --git a/cras-config/clapper/byt-max98090 b/cras-config/clapper/byt-max98090
deleted file mode 100644
index 8046a8a3..00000000
--- a/cras-config/clapper/byt-max98090
+++ /dev/null
@@ -1,107 +0,0 @@
-[Speaker]
- volume_curve = explicit
- db_at_100 = -600
- db_at_99 = -600
- db_at_98 = -700
- db_at_97 = -700
- db_at_96 = -800
- db_at_95 = -800
- db_at_94 = -900
- db_at_93 = -900
- db_at_92 = -900
- db_at_91 = -1000
- db_at_90 = -1000
- db_at_89 = -1100
- db_at_88 = -1100
- db_at_87 = -1100
- db_at_86 = -1200
- db_at_85 = -1200
- db_at_84 = -1300
- db_at_83 = -1300
- db_at_82 = -1300
- db_at_81 = -1300
- db_at_80 = -1400
- db_at_79 = -1400
- db_at_78 = -1400
- db_at_77 = -1400
- db_at_76 = -1500
- db_at_75 = -1500
- db_at_74 = -1500
- db_at_73 = -1500
- db_at_72 = -1600
- db_at_71 = -1600
- db_at_70 = -1600
- db_at_69 = -1600
- db_at_68 = -1700
- db_at_67 = -1700
- db_at_66 = -1700
- db_at_65 = -1700
- db_at_64 = -1800
- db_at_63 = -1800
- db_at_62 = -1800
- db_at_61 = -1800
- db_at_60 = -1900
- db_at_59 = -1900
- db_at_58 = -1900
- db_at_57 = -1900
- db_at_56 = -2000
- db_at_55 = -2000
- db_at_54 = -2000
- db_at_53 = -2000
- db_at_52 = -2100
- db_at_51 = -2100
- db_at_50 = -2100
- db_at_49 = -2100
- db_at_48 = -2300
- db_at_47 = -2300
- db_at_46 = -2300
- db_at_45 = -2300
- db_at_44 = -2500
- db_at_43 = -2500
- db_at_42 = -2500
- db_at_41 = -2500
- db_at_40 = -2700
- db_at_39 = -2700
- db_at_38 = -2700
- db_at_37 = -2700
- db_at_36 = -3000
- db_at_35 = -3000
- db_at_34 = -3000
- db_at_33 = -3000
- db_at_32 = -3200
- db_at_31 = -3200
- db_at_30 = -3200
- db_at_29 = -3200
- db_at_28 = -3500
- db_at_27 = -3700
- db_at_26 = -3700
- db_at_25 = -3700
- db_at_24 = -3800
- db_at_23 = -3800
- db_at_22 = -3900
- db_at_21 = -3900
- db_at_20 = -4100
- db_at_19 = -4100
- db_at_18 = -4100
- db_at_17 = -4100
- db_at_16 = -4400
- db_at_15 = -4400
- db_at_14 = -4400
- db_at_13 = -4400
- db_at_12 = -4800
- db_at_11 = -4800
- db_at_10 = -4800
- db_at_9 = -4800
- db_at_8 = -5200
- db_at_7 = -5200
- db_at_6 = -5200
- db_at_5 = -5200
- db_at_4 = -5800
- db_at_3 = -5800
- db_at_2 = -5800
- db_at_1 = -5800
- db_at_0 = -6200
-[Headphone]
- volume_curve = simple_step
- volume_step = 50
- max_volume = 0
diff --git a/cras-config/clapper/dsp.ini b/cras-config/clapper/dsp.ini
deleted file mode 100644
index 970625ad..00000000
--- a/cras-config/clapper/dsp.ini
+++ /dev/null
@@ -1,103 +0,0 @@
-[output_source]
-library=builtin
-label=source
-purpose=playback
-disable=(not (equal? dsp_name "speaker_eq"))
-output_0={src:0}
-output_1={src:1}
-
-[output_sink]
-library=builtin
-label=sink
-purpose=playback
-input_0={dst:0}
-input_1={dst:1}
-
-[eq2]
-library=builtin
-label=eq2
-input_0={src:0}
-input_1={src:1}
-output_2={intermediate:0}
-output_3={intermediate:1}
-input_4=2 ; highpass
-input_5=250 ; freq
-input_6=8 ; Q
-input_7=0 ; gain
-input_8=2 ; highpass
-input_9=250 ; freq
-input_10=8 ; Q
-input_11=0 ; gain
-input_12=6 ; peaking
-input_13=1000 ; freq
-input_14=4 ; Q
-input_15=5 ; gain
-input_16=6 ; peaking
-input_17=2000 ; freq
-input_18=1 ; Q
-input_19=-6 ; gain
-input_20=6 ; peaking
-input_21=2000 ; freq
-input_22=4 ; Q
-input_23=5 ; gain
-input_24=6 ; peaking
-input_25=10000 ; freq
-input_26=1 ; Q
-input_27=9 ; gain
-input_28=6 ; peaking
-input_29=10000 ; freq
-input_30=1 ; Q
-input_31=9 ; gain
-input_32=1 ; lowpass
-input_33=16000 ; freq
-input_34=0 ; Q
-input_35=0 ; gain
-input_36=6 ; peaking
-input_37=4000 ; freq
-input_38=4 ; Q
-input_39=4 ; gain
-input_40=0 ; none
-input_41=0 ; freq
-input_42=0 ; Q
-input_43=0 ; gain
-input_44=1 ; lowpass
-input_45=16000 ; freq
-input_46=0 ; Q
-input_47=0 ; gain
-input_48=0 ; none
-input_49=0 ; freq
-input_50=0 ; Q
-input_51=0 ; gain
-
-[drc]
-library=builtin
-label=drc
-input_0={intermediate:0}
-input_1={intermediate:1}
-output_2={dst:0}
-output_3={dst:1}
-input_4=1 ; emphasis_disabled
-input_5=0 ; f
-input_6=1 ; enable
-input_7=-30 ; threshold
-input_8=22 ; knee
-input_9=12 ; ratio
-input_10=0.003 ; attack
-input_11=0.25 ; release
-input_12=0 ; boost
-input_13=150 ; f
-input_14=1 ; enable
-input_15=-32 ; threshold
-input_16=22 ; knee
-input_17=10 ; ratio
-input_18=0.003 ; attack
-input_19=0.25 ; release
-input_20=0 ; boost
-input_21=2000 ; f
-input_22=1 ; enable
-input_23=-30 ; threshold
-input_24=22 ; knee
-input_25=12 ; ratio
-input_26=0.003 ; attack
-input_27=0.25 ; release
-input_28=0 ; boost
diff --git a/cras-config/cyan-cheets b/cras-config/cyan-cheets
new file mode 120000
index 00000000..d8fcd909
--- /dev/null
+++ b/cras-config/cyan-cheets
@@ -0,0 +1 @@
+cyan \ No newline at end of file
diff --git a/cras-config/enguarde/byt-max98090 b/cras-config/enguarde/byt-max98090
deleted file mode 100644
index 9d54d422..00000000
--- a/cras-config/enguarde/byt-max98090
+++ /dev/null
@@ -1,107 +0,0 @@
-[Speaker]
- volume_curve = explicit
- db_at_100 = -300
- db_at_99 = -350
- db_at_98 = -350
- db_at_97 = -400
- db_at_96 = -450
- db_at_95 = -450
- db_at_94 = -500
- db_at_93 = -550
- db_at_92 = -550
- db_at_91 = -600
- db_at_90 = -600
- db_at_89 = -650
- db_at_88 = -700
- db_at_87 = -700
- db_at_86 = -750
- db_at_85 = -800
- db_at_84 = -800
- db_at_83 = -850
- db_at_82 = -900
- db_at_81 = -900
- db_at_80 = -950
- db_at_79 = -1000
- db_at_78 = -1000
- db_at_77 = -1050
- db_at_76 = -1100
- db_at_75 = -1100
- db_at_74 = -1150
- db_at_73 = -1200
- db_at_72 = -1200
- db_at_71 = -1250
- db_at_70 = -1250
- db_at_69 = -1300
- db_at_68 = -1350
- db_at_67 = -1350
- db_at_66 = -1400
- db_at_65 = -1450
- db_at_64 = -1450
- db_at_63 = -1500
- db_at_62 = -1550
- db_at_61 = -1550
- db_at_60 = -1600
- db_at_59 = -1650
- db_at_58 = -1700
- db_at_57 = -1750
- db_at_56 = -1800
- db_at_55 = -1850
- db_at_54 = -1850
- db_at_53 = -1900
- db_at_52 = -1950
- db_at_51 = -2000
- db_at_50 = -2050
- db_at_49 = -2100
- db_at_48 = -2150
- db_at_47 = -2200
- db_at_46 = -2250
- db_at_45 = -2300
- db_at_44 = -2350
- db_at_43 = -2350
- db_at_42 = -2400
- db_at_41 = -2450
- db_at_40 = -2500
- db_at_39 = -2550
- db_at_38 = -2600
- db_at_37 = -2650
- db_at_36 = -2700
- db_at_35 = -2750
- db_at_34 = -2800
- db_at_33 = -2850
- db_at_32 = -2850
- db_at_31 = -2900
- db_at_30 = -2950
- db_at_29 = -3000
- db_at_28 = -3050
- db_at_27 = -3100
- db_at_26 = -3150
- db_at_25 = -3200
- db_at_24 = -3250
- db_at_23 = -3300
- db_at_22 = -3350
- db_at_21 = -3350
- db_at_20 = -3400
- db_at_19 = -3450
- db_at_18 = -3500
- db_at_17 = -3550
- db_at_16 = -3600
- db_at_15 = -3650
- db_at_14 = -3750
- db_at_13 = -3800
- db_at_12 = -3900
- db_at_11 = -3950
- db_at_10 = -4050
- db_at_9 = -4100
- db_at_8 = -4200
- db_at_7 = -4250
- db_at_6 = -4350
- db_at_5 = -4400
- db_at_4 = -4500
- db_at_3 = -4550
- db_at_2 = -4650
- db_at_1 = -4700
- db_at_0 = -4800
-[Headphone]
- volume_curve = simple_step
- volume_step = 50
- max_volume = 0
diff --git a/cras-config/enguarde/dsp.ini b/cras-config/enguarde/dsp.ini
deleted file mode 100644
index 2e7a5ee5..00000000
--- a/cras-config/enguarde/dsp.ini
+++ /dev/null
@@ -1,103 +0,0 @@
-[output_source]
-library=builtin
-label=source
-purpose=playback
-disable=(not (equal? dsp_name "speaker_eq"))
-output_0={src:0}
-output_1={src:1}
-
-[output_sink]
-library=builtin
-label=sink
-purpose=playback
-input_0={dst:0}
-input_1={dst:1}
-
-[drc]
-library=builtin
-label=drc
-input_0={src:0}
-input_1={src:1}
-output_2={intermediate:0}
-output_3={intermediate:1}
-input_4=1 ; emphasis_disabled
-input_5=0 ; f
-input_6=1 ; enable
-input_7=-31 ; threshold
-input_8=27 ; knee
-input_9=7.084 ; ratio
-input_10=0.003 ; attack
-input_11=0.25 ; release
-input_12=0 ; boost
-input_13=750 ; f
-input_14=0 ; enable
-input_15=-24 ; threshold
-input_16=30 ; knee
-input_17=12 ; ratio
-input_18=0.003 ; attack
-input_19=0.25 ; release
-input_20=0 ; boost
-input_21=1500 ; f
-input_22=0 ; enable
-input_23=-24 ; threshold
-input_24=30 ; knee
-input_25=12 ; ratio
-input_26=0.003 ; attack
-input_27=0.25 ; release
-input_28=0 ; boost
-
-[eq2]
-library=builtin
-label=eq2
-input_0={intermediate:0}
-input_1={intermediate:1}
-output_2={dst:0}
-output_3={dst:1}
-input_4=2 ; highpass
-input_5=450 ; freq
-input_6=0 ; Q
-input_7=0 ; gain
-input_8=2 ; highpass
-input_9=450 ; freq
-input_10=0 ; Q
-input_11=0 ; gain
-input_12=6 ; peaking
-input_13=750 ; freq
-input_14=6 ; Q
-input_15=-5 ; gain
-input_16=6 ; peaking
-input_17=1500 ; freq
-input_18=3 ; Q
-input_19=-3 ; gain
-input_20=6 ; peaking
-input_21=1050 ; freq
-input_22=6 ; Q
-input_23=-5 ; gain
-input_24=6 ; peaking
-input_25=510 ; freq
-input_26=6 ; Q
-input_27=-9 ; gain
-input_28=6 ; peaking
-input_29=475 ; freq
-input_30=6 ; Q
-input_31=-4 ; gain
-input_32=6 ; peaking
-input_33=1000 ; freq
-input_34=3 ; Q
-input_35=-9 ; gain
-input_36=6 ; peaking
-input_37=2025 ; freq
-input_38=3 ; Q
-input_39=-3 ; gain
-input_40=6 ; peaking
-input_41=2100 ; freq
-input_42=3 ; Q
-input_43=-6 ; gain
-input_44=0 ; none
-input_45=0 ; freq
-input_46=0 ; Q
-input_47=0 ; gain
-input_48=6 ; peaking
-input_49=725 ; freq
-input_50=6 ; Q
-input_51=-6 ; gain
diff --git a/cras-config/expresso/byt-max98090 b/cras-config/expresso/byt-max98090
deleted file mode 100644
index 1e2dfe93..00000000
--- a/cras-config/expresso/byt-max98090
+++ /dev/null
@@ -1,107 +0,0 @@
-[Speaker]
- volume_curve = explicit
- db_at_100 = -200
- db_at_99 = -250
- db_at_98 = -300
- db_at_97 = -350
- db_at_96 = -400
- db_at_95 = -450
- db_at_94 = -500
- db_at_93 = -500
- db_at_92 = -550
- db_at_91 = -600
- db_at_90 = -650
- db_at_89 = -700
- db_at_88 = -750
- db_at_87 = -800
- db_at_86 = -850
- db_at_85 = -900
- db_at_84 = -950
- db_at_83 = -1000
- db_at_82 = -1050
- db_at_81 = -1050
- db_at_80 = -1100
- db_at_79 = -1150
- db_at_78 = -1200
- db_at_77 = -1250
- db_at_76 = -1300
- db_at_75 = -1350
- db_at_74 = -1400
- db_at_73 = -1450
- db_at_72 = -1500
- db_at_71 = -1550
- db_at_70 = -1600
- db_at_69 = -1650
- db_at_68 = -1650
- db_at_67 = -1700
- db_at_66 = -1750
- db_at_65 = -1800
- db_at_64 = -1850
- db_at_63 = -1900
- db_at_62 = -1950
- db_at_61 = -2000
- db_at_60 = -2050
- db_at_59 = -2100
- db_at_58 = -2150
- db_at_57 = -2200
- db_at_56 = -2200
- db_at_55 = -2250
- db_at_54 = -2300
- db_at_53 = -2350
- db_at_52 = -2400
- db_at_51 = -2450
- db_at_50 = -2500
- db_at_49 = -2550
- db_at_48 = -2600
- db_at_47 = -2650
- db_at_46 = -2700
- db_at_45 = -2750
- db_at_44 = -2800
- db_at_43 = -2800
- db_at_42 = -2850
- db_at_41 = -2900
- db_at_40 = -2950
- db_at_39 = -3000
- db_at_38 = -3050
- db_at_37 = -3100
- db_at_36 = -3150
- db_at_35 = -3200
- db_at_34 = -3250
- db_at_33 = -3300
- db_at_32 = -3350
- db_at_31 = -3350
- db_at_30 = -3400
- db_at_29 = -3450
- db_at_28 = -3500
- db_at_27 = -3550
- db_at_26 = -3600
- db_at_25 = -3650
- db_at_24 = -3700
- db_at_23 = -3750
- db_at_22 = -3800
- db_at_21 = -3850
- db_at_20 = -3900
- db_at_19 = -3950
- db_at_18 = -3950
- db_at_17 = -4000
- db_at_16 = -4050
- db_at_15 = -4100
- db_at_14 = -4150
- db_at_13 = -4200
- db_at_12 = -4250
- db_at_11 = -4300
- db_at_10 = -4350
- db_at_9 = -4400
- db_at_8 = -4450
- db_at_7 = -4500
- db_at_6 = -4500
- db_at_5 = -4550
- db_at_4 = -4600
- db_at_3 = -4650
- db_at_2 = -4700
- db_at_1 = -4750
- db_at_0 = -4800
-[Headphone]
- volume_curve = simple_step
- volume_step = 50
- max_volume = 0
diff --git a/cras-config/expresso/dsp.ini b/cras-config/expresso/dsp.ini
deleted file mode 100644
index e91622f6..00000000
--- a/cras-config/expresso/dsp.ini
+++ /dev/null
@@ -1,103 +0,0 @@
-[output_source]
-library=builtin
-label=source
-purpose=playback
-disable=(not (equal? dsp_name "speaker_eq"))
-output_0={src:0}
-output_1={src:1}
-
-[output_sink]
-library=builtin
-label=sink
-purpose=playback
-input_0={dst:0}
-input_1={dst:1}
-
-[drc]
-library=builtin
-label=drc
-input_0={src:0}
-input_1={src:1}
-output_2={intermediate:0}
-output_3={intermediate:1}
-input_4=1 ; emphasis_disabled
-input_5=0 ; f
-input_6=1 ; enable
-input_7=-13 ; threshold
-input_8=11 ; knee
-input_9=12.238 ; ratio
-input_10=0.003 ; attack
-input_11=0.25 ; release
-input_12=1 ; boost
-input_13=400 ; f
-input_14=1 ; enable
-input_15=-16 ; threshold
-input_16=16 ; knee
-input_17=12 ; ratio
-input_18=0.003 ; attack
-input_19=0.25 ; release
-input_20=2 ; boost
-input_21=2500 ; f
-input_22=1 ; enable
-input_23=-21 ; threshold
-input_24=25 ; knee
-input_25=12 ; ratio
-input_26=0.003 ; attack
-input_27=0.25 ; release
-input_28=2 ; boost
-
-[eq2]
-library=builtin
-label=eq2
-input_0={intermediate:0}
-input_1={intermediate:1}
-output_2={dst:0}
-output_3={dst:1}
-input_4=2 ; highpass
-input_5=300 ; freq
-input_6=0 ; Q
-input_7=0 ; gain
-input_8=2 ; highpass
-input_9=300 ; freq
-input_10=0 ; Q
-input_11=0 ; gain
-input_12=6 ; peaking
-input_13=600 ; freq
-input_14=2 ; Q
-input_15=-9 ; gain
-input_16=6 ; peaking
-input_17=600 ; freq
-input_18=2 ; Q
-input_19=-9 ; gain
-input_20=6 ; peaking
-input_21=800 ; freq
-input_22=3 ; Q
-input_23=-9 ; gain
-input_24=6 ; peaking
-input_25=800 ; freq
-input_26=3 ; Q
-input_27=-9 ; gain
-input_28=6 ; peaking
-input_29=4000 ; freq
-input_30=3 ; Q
-input_31=-11 ; gain
-input_32=6 ; peaking
-input_33=4000 ; freq
-input_34=3 ; Q
-input_35=-11 ; gain
-input_36=6 ; peaking
-input_37=2200 ; freq
-input_38=2 ; Q
-input_39=-2 ; gain
-input_40=5 ; highshelf
-input_41=10000 ; freq
-input_42=1 ; Q
-input_43=2 ; gain
-input_44=5 ; highshelf
-input_45=10000 ; freq
-input_46=1 ; Q
-input_47=2 ; gain
-input_48=0 ; none
-input_49=0 ; freq
-input_50=0 ; Q
-input_51=0 ; gain
diff --git a/cras-config/glimmer/byt-max98090 b/cras-config/glimmer-cheets/byt-max98090
index 5bea604c..5bea604c 100644
--- a/cras-config/glimmer/byt-max98090
+++ b/cras-config/glimmer-cheets/byt-max98090
diff --git a/cras-config/glimmer/dsp.ini b/cras-config/glimmer-cheets/dsp.ini
index 856a220d..856a220d 100644
--- a/cras-config/glimmer/dsp.ini
+++ b/cras-config/glimmer-cheets/dsp.ini
diff --git a/cras-config/gnawty/OLAY/byt-max98090 b/cras-config/gnawty/OLAY/byt-max98090
deleted file mode 100644
index e121f1dd..00000000
--- a/cras-config/gnawty/OLAY/byt-max98090
+++ /dev/null
@@ -1,107 +0,0 @@
-[Speaker]
- volume_curve = explicit
- db_at_100 = -200
- db_at_99 = -200
- db_at_98 = -250
- db_at_97 = -250
- db_at_96 = -300
- db_at_95 = -300
- db_at_94 = -350
- db_at_93 = -350
- db_at_92 = -400
- db_at_91 = -400
- db_at_90 = -400
- db_at_89 = -450
- db_at_88 = -450
- db_at_87 = -450
- db_at_86 = -450
- db_at_85 = -500
- db_at_84 = -500
- db_at_83 = -500
- db_at_82 = -550
- db_at_81 = -550
- db_at_80 = -600
- db_at_79 = -600
- db_at_78 = -650
- db_at_77 = -650
- db_at_76 = -700
- db_at_75 = -700
- db_at_74 = -750
- db_at_73 = -750
- db_at_72 = -800
- db_at_71 = -800
- db_at_70 = -850
- db_at_69 = -850
- db_at_68 = -900
- db_at_67 = -900
- db_at_66 = -950
- db_at_65 = -950
- db_at_64 = -1000
- db_at_63 = -1000
- db_at_62 = -1050
- db_at_61 = -1050
- db_at_60 = -1100
- db_at_59 = -1100
- db_at_58 = -1150
- db_at_57 = -1150
- db_at_56 = -1200
- db_at_55 = -1200
- db_at_54 = -1250
- db_at_53 = -1250
- db_at_52 = -1300
- db_at_51 = -1300
- db_at_50 = -1350
- db_at_49 = -1350
- db_at_48 = -1400
- db_at_47 = -1400
- db_at_46 = -1450
- db_at_45 = -1450
- db_at_44 = -1500
- db_at_43 = -1550
- db_at_42 = -1600
- db_at_41 = -1650
- db_at_40 = -1700
- db_at_39 = -1750
- db_at_38 = -1850
- db_at_37 = -1900
- db_at_36 = -2000
- db_at_35 = -2100
- db_at_34 = -2200
- db_at_33 = -2300
- db_at_32 = -2400
- db_at_31 = -2450
- db_at_30 = -2500
- db_at_29 = -2550
- db_at_28 = -2600
- db_at_27 = -2650
- db_at_26 = -2700
- db_at_25 = -2750
- db_at_24 = -2800
- db_at_23 = -2850
- db_at_22 = -2950
- db_at_21 = -3000
- db_at_20 = -3100
- db_at_19 = -3150
- db_at_18 = -3250
- db_at_17 = -3300
- db_at_16 = -3400
- db_at_15 = -3450
- db_at_14 = -3550
- db_at_13 = -3600
- db_at_12 = -3700
- db_at_11 = -3750
- db_at_10 = -3850
- db_at_9 = -3900
- db_at_8 = -4000
- db_at_7 = -4050
- db_at_6 = -4150
- db_at_5 = -4200
- db_at_4 = -4300
- db_at_3 = -4350
- db_at_2 = -4450
- db_at_1 = -4500
- db_at_0 = -4600
-[Headphone]
- volume_curve = simple_step
- volume_step = 70
- max_volume = 0
diff --git a/cras-config/gnawty/OLAY/dsp.ini b/cras-config/gnawty/OLAY/dsp.ini
deleted file mode 100644
index 5f9a3490..00000000
--- a/cras-config/gnawty/OLAY/dsp.ini
+++ /dev/null
@@ -1,111 +0,0 @@
-[output_source]
-library=builtin
-label=source
-purpose=playback
-disable=(not (equal? dsp_name "speaker_eq"))
-output_0={src:0}
-output_1={src:1}
-
-[output_sink]
-library=builtin
-label=sink
-purpose=playback
-input_0={dst:0}
-input_1={dst:1}
-
-[drc]
-library=builtin
-label=drc
-input_0={src:0}
-input_1={src:1}
-output_2={intermediate:0}
-output_3={intermediate:1}
-input_4=1 ; emphasis_disabled
-input_5=0 ; f
-input_6=1 ; enable
-input_7=-27 ; threshold
-input_8=31 ; knee
-input_9=15.018 ; ratio
-input_10=0.003 ; attack
-input_11=0.25 ; release
-input_12=1.5 ; boost
-input_13=250 ; f
-input_14=1 ; enable
-input_15=-32 ; threshold
-input_16=34 ; knee
-input_17=12.817 ; ratio
-input_18=0.003 ; attack
-input_19=0.25 ; release
-input_20=2 ; boost
-input_21=1800 ; f
-input_22=1 ; enable
-input_23=-38 ; threshold
-input_24=37 ; knee
-input_25=9.921 ; ratio
-input_26=0.003 ; attack
-input_27=0.25 ; release
-input_28=2.5 ; boost
-
-[eq2]
-library=builtin
-label=eq2
-input_0={intermediate:0}
-input_1={intermediate:1}
-output_2={dst:0}
-output_3={dst:1}
-input_4=2 ; highpass
-input_5=250 ; freq
-input_6=0 ; Q
-input_7=-8.8 ; gain
-input_8=2 ; highpass
-input_9=250 ; freq
-input_10=0 ; Q
-input_11=-8.3 ; gain
-input_12=6 ; peaking
-input_13=649 ; freq
-input_14=2.5 ; Q
-input_15=-8.8 ; gain
-input_16=6 ; peaking
-input_17=649 ; freq
-input_18=2.5 ; Q
-input_19=-8.8 ; gain
-input_20=6 ; peaking
-input_21=1200 ; freq
-input_22=3.5 ; Q
-input_23=2.4 ; gain
-input_24=6 ; peaking
-input_25=1200 ; freq
-input_26=3.5 ; Q
-input_27=2.4 ; gain
-input_28=6 ; peaking
-input_29=10000 ; freq
-input_30=4.5 ; Q
-input_31=4.5 ; gain
-input_32=6 ; peaking
-input_33=10000 ; freq
-input_34=4.5 ; Q
-input_35=4.5 ; gain
-input_36=6 ; peaking
-input_37=6070 ; freq
-input_38=3.8 ; Q
-input_39=3.5 ; gain
-input_40=6 ; peaking
-input_41=6070 ; freq
-input_42=3.8 ; Q
-input_43=3 ; gain
-input_44=5 ; highshelf
-input_45=8083 ; freq
-input_46=1 ; Q
-input_47=3.5 ; gain
-input_48=5 ; highshelf
-input_49=8083 ; freq
-input_50=1 ; Q
-input_51=3.5 ; gain
-input_52=6 ; peaking
-input_53=3232 ; freq
-input_54=3.3685 ; Q
-input_55=-7.3 ; gain
-input_56=6 ; peaking
-input_57=3232 ; freq
-input_58=3.3685 ; Q
-input_59=-7.3 ; gain
diff --git a/cras-config/gnawty/byt-max98090 b/cras-config/gnawty/byt-max98090
deleted file mode 100644
index 1e2dfe93..00000000
--- a/cras-config/gnawty/byt-max98090
+++ /dev/null
@@ -1,107 +0,0 @@
-[Speaker]
- volume_curve = explicit
- db_at_100 = -200
- db_at_99 = -250
- db_at_98 = -300
- db_at_97 = -350
- db_at_96 = -400
- db_at_95 = -450
- db_at_94 = -500
- db_at_93 = -500
- db_at_92 = -550
- db_at_91 = -600
- db_at_90 = -650
- db_at_89 = -700
- db_at_88 = -750
- db_at_87 = -800
- db_at_86 = -850
- db_at_85 = -900
- db_at_84 = -950
- db_at_83 = -1000
- db_at_82 = -1050
- db_at_81 = -1050
- db_at_80 = -1100
- db_at_79 = -1150
- db_at_78 = -1200
- db_at_77 = -1250
- db_at_76 = -1300
- db_at_75 = -1350
- db_at_74 = -1400
- db_at_73 = -1450
- db_at_72 = -1500
- db_at_71 = -1550
- db_at_70 = -1600
- db_at_69 = -1650
- db_at_68 = -1650
- db_at_67 = -1700
- db_at_66 = -1750
- db_at_65 = -1800
- db_at_64 = -1850
- db_at_63 = -1900
- db_at_62 = -1950
- db_at_61 = -2000
- db_at_60 = -2050
- db_at_59 = -2100
- db_at_58 = -2150
- db_at_57 = -2200
- db_at_56 = -2200
- db_at_55 = -2250
- db_at_54 = -2300
- db_at_53 = -2350
- db_at_52 = -2400
- db_at_51 = -2450
- db_at_50 = -2500
- db_at_49 = -2550
- db_at_48 = -2600
- db_at_47 = -2650
- db_at_46 = -2700
- db_at_45 = -2750
- db_at_44 = -2800
- db_at_43 = -2800
- db_at_42 = -2850
- db_at_41 = -2900
- db_at_40 = -2950
- db_at_39 = -3000
- db_at_38 = -3050
- db_at_37 = -3100
- db_at_36 = -3150
- db_at_35 = -3200
- db_at_34 = -3250
- db_at_33 = -3300
- db_at_32 = -3350
- db_at_31 = -3350
- db_at_30 = -3400
- db_at_29 = -3450
- db_at_28 = -3500
- db_at_27 = -3550
- db_at_26 = -3600
- db_at_25 = -3650
- db_at_24 = -3700
- db_at_23 = -3750
- db_at_22 = -3800
- db_at_21 = -3850
- db_at_20 = -3900
- db_at_19 = -3950
- db_at_18 = -3950
- db_at_17 = -4000
- db_at_16 = -4050
- db_at_15 = -4100
- db_at_14 = -4150
- db_at_13 = -4200
- db_at_12 = -4250
- db_at_11 = -4300
- db_at_10 = -4350
- db_at_9 = -4400
- db_at_8 = -4450
- db_at_7 = -4500
- db_at_6 = -4500
- db_at_5 = -4550
- db_at_4 = -4600
- db_at_3 = -4650
- db_at_2 = -4700
- db_at_1 = -4750
- db_at_0 = -4800
-[Headphone]
- volume_curve = simple_step
- volume_step = 50
- max_volume = 0
diff --git a/cras-config/gnawty/dsp.ini b/cras-config/gnawty/dsp.ini
deleted file mode 100644
index e91622f6..00000000
--- a/cras-config/gnawty/dsp.ini
+++ /dev/null
@@ -1,103 +0,0 @@
-[output_source]
-library=builtin
-label=source
-purpose=playback
-disable=(not (equal? dsp_name "speaker_eq"))
-output_0={src:0}
-output_1={src:1}
-
-[output_sink]
-library=builtin
-label=sink
-purpose=playback
-input_0={dst:0}
-input_1={dst:1}
-
-[drc]
-library=builtin
-label=drc
-input_0={src:0}
-input_1={src:1}
-output_2={intermediate:0}
-output_3={intermediate:1}
-input_4=1 ; emphasis_disabled
-input_5=0 ; f
-input_6=1 ; enable
-input_7=-13 ; threshold
-input_8=11 ; knee
-input_9=12.238 ; ratio
-input_10=0.003 ; attack
-input_11=0.25 ; release
-input_12=1 ; boost
-input_13=400 ; f
-input_14=1 ; enable
-input_15=-16 ; threshold
-input_16=16 ; knee
-input_17=12 ; ratio
-input_18=0.003 ; attack
-input_19=0.25 ; release
-input_20=2 ; boost
-input_21=2500 ; f
-input_22=1 ; enable
-input_23=-21 ; threshold
-input_24=25 ; knee
-input_25=12 ; ratio
-input_26=0.003 ; attack
-input_27=0.25 ; release
-input_28=2 ; boost
-
-[eq2]
-library=builtin
-label=eq2
-input_0={intermediate:0}
-input_1={intermediate:1}
-output_2={dst:0}
-output_3={dst:1}
-input_4=2 ; highpass
-input_5=300 ; freq
-input_6=0 ; Q
-input_7=0 ; gain
-input_8=2 ; highpass
-input_9=300 ; freq
-input_10=0 ; Q
-input_11=0 ; gain
-input_12=6 ; peaking
-input_13=600 ; freq
-input_14=2 ; Q
-input_15=-9 ; gain
-input_16=6 ; peaking
-input_17=600 ; freq
-input_18=2 ; Q
-input_19=-9 ; gain
-input_20=6 ; peaking
-input_21=800 ; freq
-input_22=3 ; Q
-input_23=-9 ; gain
-input_24=6 ; peaking
-input_25=800 ; freq
-input_26=3 ; Q
-input_27=-9 ; gain
-input_28=6 ; peaking
-input_29=4000 ; freq
-input_30=3 ; Q
-input_31=-11 ; gain
-input_32=6 ; peaking
-input_33=4000 ; freq
-input_34=3 ; Q
-input_35=-11 ; gain
-input_36=6 ; peaking
-input_37=2200 ; freq
-input_38=2 ; Q
-input_39=-2 ; gain
-input_40=5 ; highshelf
-input_41=10000 ; freq
-input_42=1 ; Q
-input_43=2 ; gain
-input_44=5 ; highshelf
-input_45=10000 ; freq
-input_46=1 ; Q
-input_47=2 ; gain
-input_48=0 ; none
-input_49=0 ; freq
-input_50=0 ; Q
-input_51=0 ; gain
diff --git a/cras-config/gnawty/get_device_config_dir b/cras-config/gnawty/get_device_config_dir
deleted file mode 100755
index 95adbc08..00000000
--- a/cras-config/gnawty/get_device_config_dir
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/sh
-# Copyright 2015 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.
-
-VPD_CACHE_FILE="/mnt/stateful_partition/unencrypted/cache/vpd/filtered.txt"
-if [ -e "${VPD_CACHE_FILE}" ]; then
- CUSTOMIZATION_ID="$(sed -nre 's/^"customization_id"="(.+)"$/\1/p' \
- <"${VPD_CACHE_FILE}")"
- SERIES="${CUSTOMIZATION_ID##*-}"
-fi
-echo "/etc/cras/${SERIES}" \ No newline at end of file
diff --git a/cras-config/heli/byt-max98090 b/cras-config/heli/byt-max98090
deleted file mode 100644
index 7e8b38b0..00000000
--- a/cras-config/heli/byt-max98090
+++ /dev/null
@@ -1,107 +0,0 @@
-[Speaker]
- volume_curve = explicit
- db_at_100 = 0
- db_at_99 = 0
- db_at_98 = -50
- db_at_97 = -50
- db_at_96 = -50
- db_at_95 = -100
- db_at_94 = -100
- db_at_93 = -150
- db_at_92 = -150
- db_at_91 = -150
- db_at_90 = -200
- db_at_89 = -200
- db_at_88 = -200
- db_at_87 = -250
- db_at_86 = -250
- db_at_85 = -300
- db_at_84 = -300
- db_at_83 = -350
- db_at_82 = -350
- db_at_81 = -400
- db_at_80 = -450
- db_at_79 = -500
- db_at_78 = -500
- db_at_77 = -550
- db_at_76 = -600
- db_at_75 = -650
- db_at_74 = -650
- db_at_73 = -700
- db_at_72 = -750
- db_at_71 = -800
- db_at_70 = -800
- db_at_69 = -850
- db_at_68 = -900
- db_at_67 = -950
- db_at_66 = -950
- db_at_65 = -1000
- db_at_64 = -1050
- db_at_63 = -1100
- db_at_62 = -1100
- db_at_61 = -1150
- db_at_60 = -1200
- db_at_59 = -1300
- db_at_58 = -1350
- db_at_57 = -1450
- db_at_56 = -1500
- db_at_55 = -1600
- db_at_54 = -1650
- db_at_53 = -1750
- db_at_52 = -1800
- db_at_51 = -1900
- db_at_50 = -1950
- db_at_49 = -2050
- db_at_48 = -2100
- db_at_47 = -2200
- db_at_46 = -2250
- db_at_45 = -2350
- db_at_44 = -2450
- db_at_43 = -2500
- db_at_42 = -2600
- db_at_41 = -2650
- db_at_40 = -2750
- db_at_39 = -2800
- db_at_38 = -2900
- db_at_37 = -2950
- db_at_36 = -3050
- db_at_35 = -3100
- db_at_34 = -3200
- db_at_33 = -3250
- db_at_32 = -3350
- db_at_31 = -3400
- db_at_30 = -3500
- db_at_29 = -3600
- db_at_28 = -3650
- db_at_27 = -3750
- db_at_26 = -3800
- db_at_25 = -3900
- db_at_24 = -3950
- db_at_23 = -4050
- db_at_22 = -4100
- db_at_21 = -4200
- db_at_20 = -4250
- db_at_19 = -4350
- db_at_18 = -4400
- db_at_17 = -4500
- db_at_16 = -4550
- db_at_15 = -4650
- db_at_14 = -4750
- db_at_13 = -4800
- db_at_12 = -4900
- db_at_11 = -4950
- db_at_10 = -5050
- db_at_9 = -5100
- db_at_8 = -5200
- db_at_7 = -5250
- db_at_6 = -5350
- db_at_5 = -5400
- db_at_4 = -5500
- db_at_3 = -5550
- db_at_2 = -5650
- db_at_1 = -5700
- db_at_0 = -5800
-[Headphone]
- volume_curve = simple_step
- volume_step = 70
- max_volume = 0
diff --git a/cras-config/heli/dsp.ini b/cras-config/heli/dsp.ini
deleted file mode 100644
index f425b505..00000000
--- a/cras-config/heli/dsp.ini
+++ /dev/null
@@ -1,111 +0,0 @@
-[output_source]
-library=builtin
-label=source
-purpose=playback
-disable=(not (equal? dsp_name "speaker_eq"))
-output_0={src:0}
-output_1={src:1}
-
-[output_sink]
-library=builtin
-label=sink
-purpose=playback
-input_0={dst:0}
-input_1={dst:1}
-
-[drc]
-library=builtin
-label=drc
-input_0={src:0}
-input_1={src:1}
-output_2={intermediate:0}
-output_3={intermediate:1}
-input_4=1 ; emphasis_disabled
-input_5=0 ; f
-input_6=1 ; enable
-input_7=-24 ; threshold
-input_8=11 ; knee
-input_9=12.2 ; ratio
-input_10=0.003 ; attack
-input_11=0.25 ; release
-input_12=1 ; boost
-input_13=400 ; f
-input_14=1 ; enable
-input_15=-22 ; threshold
-input_16=13 ; knee
-input_17=12 ; ratio
-input_18=0.003 ; attack
-input_19=0.25 ; release
-input_20=1 ; boost
-input_21=2000 ; f
-input_22=1 ; enable
-input_23=-27 ; threshold
-input_24=22 ; knee
-input_25=12 ; ratio
-input_26=0.003 ; attack
-input_27=0.25 ; release
-input_28=0 ; boost
-
-[eq2]
-library=builtin
-label=eq2
-input_0={intermediate:0}
-input_1={intermediate:1}
-output_2={dst:0}
-output_3={dst:1}
-input_4=6 ; peaking
-input_5=1800 ; freq
-input_6=3 ; Q
-input_7=-7 ; gain
-input_8=6 ; peaking
-input_9=1800 ; freq
-input_10=3 ; Q
-input_11=-7 ; gain
-input_12=6 ; peaking
-input_13=3500 ; freq
-input_14=3 ; Q
-input_15=-6 ; gain
-input_16=6 ; peaking
-input_17=3500 ; freq
-input_18=3 ; Q
-input_19=-6 ; gain
-input_20=6 ; peaking
-input_21=750 ; freq
-input_22=5 ; Q
-input_23=-11 ; gain
-input_24=6 ; peaking
-input_25=750 ; freq
-input_26=5 ; Q
-input_27=-11 ; gain
-input_28=6 ; peaking
-input_29=11000 ; freq
-input_30=3 ; Q
-input_31=-12 ; gain
-input_32=6 ; peaking
-input_33=11000 ; freq
-input_34=3 ; Q
-input_35=-12 ; gain
-input_36=6 ; peaking
-input_37=8000 ; freq
-input_38=3 ; Q
-input_39=-10 ; gain
-input_40=6 ; peaking
-input_41=8000 ; freq
-input_42=3 ; Q
-input_43=-10 ; gain
-input_44=2 ; highpass
-input_45=350 ; freq
-input_46=0 ; Q
-input_47=2 ; gain
-input_48=2 ; highpass
-input_49=350 ; freq
-input_50=0 ; Q
-input_51=2 ; gain
-input_52=6 ; peaking
-input_53=1000 ; freq
-input_54=5 ; Q
-input_55=-7 ; gain
-input_56=6 ; peaking
-input_57=1000 ; freq
-input_58=5 ; Q
-input_59=-7 ; gain
diff --git a/cras-config/kip/KIP14/byt-max98090 b/cras-config/kip/KIP14/byt-max98090
deleted file mode 100644
index b87abda4..00000000
--- a/cras-config/kip/KIP14/byt-max98090
+++ /dev/null
@@ -1,107 +0,0 @@
-[Speaker]
- volume_curve = explicit
- db_at_100 = 0
- db_at_99 = -75
- db_at_98 = -75
- db_at_97 = -75
- db_at_96 = -75
- db_at_95 = -75
- db_at_94 = -150
- db_at_93 = -150
- db_at_92 = -150
- db_at_91 = -150
- db_at_90 = -225
- db_at_89 = -225
- db_at_88 = -225
- db_at_87 = -225
- db_at_86 = -300
- db_at_85 = -300
- db_at_84 = -300
- db_at_83 = -300
- db_at_82 = -375
- db_at_81 = -375
- db_at_80 = -450
- db_at_79 = -450
- db_at_78 = -450
- db_at_77 = -450
- db_at_76 = -525
- db_at_75 = -525
- db_at_74 = -600
- db_at_73 = -600
- db_at_72 = -600
- db_at_71 = -600
- db_at_70 = -675
- db_at_69 = -675
- db_at_68 = -675
- db_at_67 = -675
- db_at_66 = -750
- db_at_65 = -750
- db_at_64 = -825
- db_at_63 = -825
- db_at_62 = -825
- db_at_61 = -825
- db_at_60 = -900
- db_at_59 = -900
- db_at_58 = -900
- db_at_57 = -900
- db_at_56 = -975
- db_at_55 = -975
- db_at_54 = -1050
- db_at_53 = -1050
- db_at_52 = -1050
- db_at_51 = -1050
- db_at_50 = -1125
- db_at_49 = -1125
- db_at_48 = -1200
- db_at_47 = -1200
- db_at_46 = -1200
- db_at_45 = -1200
- db_at_44 = -1275
- db_at_43 = -1275
- db_at_42 = -1275
- db_at_41 = -1275
- db_at_40 = -1350
- db_at_39 = -1425
- db_at_38 = -1425
- db_at_37 = -1500
- db_at_36 = -1500
- db_at_35 = -1575
- db_at_34 = -1650
- db_at_33 = -1650
- db_at_32 = -1725
- db_at_31 = -1800
- db_at_30 = -1800
- db_at_29 = -1800
- db_at_28 = -1875
- db_at_27 = -2025
- db_at_26 = -2100
- db_at_25 = -2100
- db_at_24 = -2175
- db_at_23 = -2250
- db_at_22 = -2400
- db_at_21 = -2475
- db_at_20 = -2550
- db_at_19 = -2625
- db_at_18 = -2700
- db_at_17 = -2850
- db_at_16 = -2925
- db_at_15 = -3000
- db_at_14 = -3075
- db_at_13 = -3225
- db_at_12 = -3300
- db_at_11 = -3375
- db_at_10 = -3450
- db_at_9 = -3600
- db_at_8 = -3675
- db_at_7 = -3750
- db_at_6 = -3825
- db_at_5 = -3900
- db_at_4 = -4050
- db_at_3 = -4050
- db_at_2 = -4275
- db_at_1 = -4500
- db_at_0 = -4800
-[Headphone]
- volume_curve = simple_step
- volume_step = 50
- max_volume = -800
diff --git a/cras-config/kip/KIP14/dsp.ini b/cras-config/kip/KIP14/dsp.ini
deleted file mode 100644
index c5d562d0..00000000
--- a/cras-config/kip/KIP14/dsp.ini
+++ /dev/null
@@ -1,119 +0,0 @@
-[output_source]
-library=builtin
-label=source
-purpose=playback
-disable=(not (equal? dsp_name "speaker_eq"))
-output_0={src:0}
-output_1={src:1}
-
-[output_sink]
-library=builtin
-label=sink
-purpose=playback
-input_0={dst:0}
-input_1={dst:1}
-
-[drc]
-library=builtin
-label=drc
-input_0={src:0}
-input_1={src:1}
-output_2={intermediate:0}
-output_3={intermediate:1}
-input_4=1 ; emphasis_disabled
-input_5=0 ; f
-input_6=1 ; enable
-input_7=-24 ; threshold
-input_8=30 ; knee
-input_9=12 ; ratio
-input_10=0.003 ; attack
-input_11=0.25 ; release
-input_12=0 ; boost
-input_13=200 ; f
-input_14=1 ; enable
-input_15=-24 ; threshold
-input_16=30 ; knee
-input_17=12 ; ratio
-input_18=0.003 ; attack
-input_19=0.25 ; release
-input_20=0 ; boost
-input_21=2000 ; f
-input_22=1 ; enable
-input_23=-24 ; threshold
-input_24=30 ; knee
-input_25=12 ; ratio
-input_26=0.003 ; attack
-input_27=0.25 ; release
-input_28=0 ; boost
-
-[eq2]
-library=builtin
-label=eq2
-input_0={intermediate:0}
-input_1={intermediate:1}
-output_2={dst:0}
-output_3={dst:1}
-input_4=2 ; highpass
-input_5=200 ; freq
-input_6=0 ; Q
-input_7=0 ; gain
-input_8=2 ; highpass
-input_9=200 ; freq
-input_10=0 ; Q
-input_11=0 ; gain
-input_12=6 ; peaking
-input_13=650 ; freq
-input_14=1 ; Q
-input_15=-6 ; gain
-input_16=6 ; peaking
-input_17=650 ; freq
-input_18=1 ; Q
-input_19=-6 ; gain
-input_20=6 ; peaking
-input_21=1000 ; freq
-input_22=1 ; Q
-input_23=-6 ; gain
-input_24=6 ; peaking
-input_25=1000 ; freq
-input_26=1 ; Q
-input_27=-6 ; gain
-input_28=6 ; peaking
-input_29=250 ; freq
-input_30=1 ; Q
-input_31=0 ; gain
-input_32=6 ; peaking
-input_33=250 ; freq
-input_34=1 ; Q
-input_35=0 ; gain
-input_36=6 ; peaking
-input_37=350 ; freq
-input_38=1 ; Q
-input_39=0 ; gain
-input_40=6 ; peaking
-input_41=350 ; freq
-input_42=1 ; Q
-input_43=0 ; gain
-input_44=6 ; peaking
-input_45=350 ; freq
-input_46=1 ; Q
-input_47=0 ; gain
-input_48=6 ; peaking
-input_49=350 ; freq
-input_50=1 ; Q
-input_51=0 ; gain
-input_52=6 ; peaking
-input_53=8000 ; freq
-input_54=1 ; Q
-input_55=0 ; gain
-input_56=6 ; peaking
-input_57=8000 ; freq
-input_58=1 ; Q
-input_59=0 ; gain
-input_60=6 ; peaking
-input_61=10000 ; freq
-input_62=1 ; Q
-input_63=4 ; gain
-input_64=6 ; peaking
-input_65=10000 ; freq
-input_66=1 ; Q
-input_67=4 ; gain
diff --git a/cras-config/kip/dsp.ini b/cras-config/kip/dsp.ini
deleted file mode 100644
index 7c94fc0d..00000000
--- a/cras-config/kip/dsp.ini
+++ /dev/null
@@ -1,111 +0,0 @@
-[output_source]
-library=builtin
-label=source
-purpose=playback
-disable=(not (equal? dsp_name "speaker_eq"))
-output_0={src:0}
-output_1={src:1}
-
-[output_sink]
-library=builtin
-label=sink
-purpose=playback
-input_0={dst:0}
-input_1={dst:1}
-
-[drc]
-library=builtin
-label=drc
-input_0={src:0}
-input_1={src:1}
-output_2={intermediate:0}
-output_3={intermediate:1}
-input_4=1 ; emphasis_disabled
-input_5=0 ; f
-input_6=1 ; enable
-input_7=-24 ; threshold
-input_8=30 ; knee
-input_9=12 ; ratio
-input_10=0.003 ; attack
-input_11=0.25 ; release
-input_12=2 ; boost
-input_13=200 ; f
-input_14=1 ; enable
-input_15=-24 ; threshold
-input_16=30 ; knee
-input_17=12 ; ratio
-input_18=0.003 ; attack
-input_19=0.25 ; release
-input_20=2 ; boost
-input_21=2000 ; f
-input_22=1 ; enable
-input_23=-24 ; threshold
-input_24=30 ; knee
-input_25=12 ; ratio
-input_26=0.003 ; attack
-input_27=0.25 ; release
-input_28=2 ; boost
-
-[eq2]
-library=builtin
-label=eq2
-input_0={intermediate:0}
-input_1={intermediate:1}
-output_2={dst:0}
-output_3={dst:1}
-input_4=2 ; highpass
-input_5=200 ; freq
-input_6=0 ; Q
-input_7=0 ; gain
-input_8=2 ; highpass
-input_9=200 ; freq
-input_10=0 ; Q
-input_11=0 ; gain
-input_12=6 ; peaking
-input_13=350 ; freq
-input_14=1 ; Q
-input_15=3 ; gain
-input_16=6 ; peaking
-input_17=350 ; freq
-input_18=1 ; Q
-input_19=3 ; gain
-input_20=6 ; peaking
-input_21=1000 ; freq
-input_22=1 ; Q
-input_23=-8 ; gain
-input_24=6 ; peaking
-input_25=1000 ; freq
-input_26=1 ; Q
-input_27=-8 ; gain
-input_28=6 ; peaking
-input_29=5000 ; freq
-input_30=1 ; Q
-input_31=3 ; gain
-input_32=6 ; peaking
-input_33=5000 ; freq
-input_34=1 ; Q
-input_35=3 ; gain
-input_36=6 ; peaking
-input_37=8000 ; freq
-input_38=1 ; Q
-input_39=3 ; gain
-input_40=6 ; peaking
-input_41=8000 ; freq
-input_42=1 ; Q
-input_43=3 ; gain
-input_44=6 ; peaking
-input_45=10000 ; freq
-input_46=1 ; Q
-input_47=3 ; gain
-input_48=6 ; peaking
-input_49=10000 ; freq
-input_50=1 ; Q
-input_51=3 ; gain
-input_52=6 ; peaking
-input_53=2000 ; freq
-input_54=1 ; Q
-input_55=-2 ; gain
-input_56=6 ; peaking
-input_57=2000 ; freq
-input_58=1 ; Q
-input_59=-2 ; gain
diff --git a/cras-config/kip/get_device_config_dir b/cras-config/kip/get_device_config_dir
deleted file mode 100755
index 95adbc08..00000000
--- a/cras-config/kip/get_device_config_dir
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/sh
-# Copyright 2015 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.
-
-VPD_CACHE_FILE="/mnt/stateful_partition/unencrypted/cache/vpd/filtered.txt"
-if [ -e "${VPD_CACHE_FILE}" ]; then
- CUSTOMIZATION_ID="$(sed -nre 's/^"customization_id"="(.+)"$/\1/p' \
- <"${VPD_CACHE_FILE}")"
- SERIES="${CUSTOMIZATION_ID##*-}"
-fi
-echo "/etc/cras/${SERIES}" \ No newline at end of file
diff --git a/cras-config/orco/byt-max98090 b/cras-config/orco/byt-max98090
deleted file mode 100644
index 828e5148..00000000
--- a/cras-config/orco/byt-max98090
+++ /dev/null
@@ -1,107 +0,0 @@
-[Speaker]
- volume_curve = explicit
- db_at_100 = -420
- db_at_99 = -450
- db_at_98 = -500
- db_at_97 = -550
- db_at_96 = -600
- db_at_95 = -650
- db_at_94 = -700
- db_at_93 = -750
- db_at_92 = -750
- db_at_91 = -800
- db_at_90 = -850
- db_at_89 = -900
- db_at_88 = -950
- db_at_87 = -1000
- db_at_86 = -1050
- db_at_85 = -1100
- db_at_84 = -1100
- db_at_83 = -1150
- db_at_82 = -1200
- db_at_81 = -1250
- db_at_80 = -1300
- db_at_79 = -1350
- db_at_78 = -1400
- db_at_77 = -1450
- db_at_76 = -1450
- db_at_75 = -1500
- db_at_74 = -1550
- db_at_73 = -1600
- db_at_72 = -1650
- db_at_71 = -1700
- db_at_70 = -1750
- db_at_69 = -1800
- db_at_68 = -1800
- db_at_67 = -1850
- db_at_66 = -1900
- db_at_65 = -1950
- db_at_64 = -2000
- db_at_63 = -2050
- db_at_62 = -2100
- db_at_61 = -2150
- db_at_60 = -2150
- db_at_59 = -2200
- db_at_58 = -2250
- db_at_57 = -2300
- db_at_56 = -2350
- db_at_55 = -2400
- db_at_54 = -2450
- db_at_53 = -2500
- db_at_52 = -2500
- db_at_51 = -2550
- db_at_50 = -2600
- db_at_49 = -2650
- db_at_48 = -2700
- db_at_47 = -2750
- db_at_46 = -2800
- db_at_45 = -2850
- db_at_44 = -2850
- db_at_43 = -2900
- db_at_42 = -2950
- db_at_41 = -3000
- db_at_40 = -3050
- db_at_39 = -3100
- db_at_38 = -3150
- db_at_37 = -3200
- db_at_36 = -3200
- db_at_35 = -3250
- db_at_34 = -3300
- db_at_33 = -3350
- db_at_32 = -3400
- db_at_31 = -3450
- db_at_30 = -3500
- db_at_29 = -3550
- db_at_28 = -3550
- db_at_27 = -3600
- db_at_26 = -3650
- db_at_25 = -3700
- db_at_24 = -3750
- db_at_23 = -3800
- db_at_22 = -3850
- db_at_21 = -3900
- db_at_20 = -3900
- db_at_19 = -3950
- db_at_18 = -4000
- db_at_17 = -4050
- db_at_16 = -4100
- db_at_15 = -4150
- db_at_14 = -4200
- db_at_13 = -4250
- db_at_12 = -4250
- db_at_11 = -4300
- db_at_10 = -4350
- db_at_9 = -4400
- db_at_8 = -4450
- db_at_7 = -4500
- db_at_6 = -4550
- db_at_5 = -4600
- db_at_4 = -4600
- db_at_3 = -4650
- db_at_2 = -4700
- db_at_1 = -4750
- db_at_0 = -4800
-[Headphone]
- volume_curve = simple_step
- volume_step = 70
- max_volume = 0
diff --git a/cras-config/orco/dsp.ini b/cras-config/orco/dsp.ini
deleted file mode 100644
index acba61fe..00000000
--- a/cras-config/orco/dsp.ini
+++ /dev/null
@@ -1,62 +0,0 @@
-[output_source]
-library=builtin
-label=source
-purpose=playback
-disable=(not (equal? dsp_name "speaker_eq"))
-output_0={src:0}
-output_1={src:1}
-
-[output_sink]
-library=builtin
-label=sink
-purpose=playback
-input_0={dst:0}
-input_1={dst:1}
-
-[eq2]
-library=builtin
-label=eq2
-input_0={src:0}
-input_1={src:1}
-output_2={dst:0}
-output_3={dst:1}
-input_4=6 ; peaking
-input_5=760 ; freq
-input_6=5.7989 ; Q
-input_7=-4.6 ; gain
-input_8=6 ; peaking
-input_9=750 ; freq
-input_10=4.9917 ; Q
-input_11=-4.1 ; gain
-input_12=6 ; peaking
-input_13=3300 ; freq
-input_14=3.4615 ; Q
-input_15=-4.6 ; gain
-input_16=6 ; peaking
-input_17=2300 ; freq
-input_18=1.18 ; Q
-input_19=1.2 ; gain
-input_20=6 ; peaking
-input_21=5200 ; freq
-input_22=3.2775 ; Q
-input_23=0.7 ; gain
-input_24=6 ; peaking
-input_25=6300 ; freq
-input_26=1.4737 ; Q
-input_27=-4 ; gain
-input_28=6 ; peaking
-input_29=2200 ; freq
-input_30=1.4737 ; Q
-input_31=1.7 ; gain
-input_32=6 ; peaking
-input_33=8600 ; freq
-input_34=1.807 ; Q
-input_35=1.2 ; gain
-input_36=2 ; highpass
-input_37=450 ; freq
-input_38=0 ; Q
-input_39=1.2 ; gain
-input_40=2 ; highpass
-input_41=450 ; freq
-input_42=0 ; Q
-input_43=0 ; gain
diff --git a/cras-config/quawks/byt-max98090 b/cras-config/quawks/byt-max98090
deleted file mode 100644
index 64be2b54..00000000
--- a/cras-config/quawks/byt-max98090
+++ /dev/null
@@ -1,206 +0,0 @@
-[Speaker]
- volume_curve = explicit
- db_at_100 = -400
- db_at_99 = -400
- db_at_98 = -400
- db_at_97 = -556
- db_at_96 = -556
- db_at_95 = -556
- db_at_94 = -712
- db_at_93 = -712
- db_at_92 = -712
- db_at_91 = -712
- db_at_90 = -712
- db_at_89 = -868
- db_at_88 = -868
- db_at_87 = -868
- db_at_86 = -868
- db_at_85 = -1024
- db_at_84 = -1024
- db_at_83 = -1024
- db_at_82 = -1024
- db_at_81 = -1180
- db_at_80 = -1180
- db_at_79 = -1180
- db_at_78 = -1180
- db_at_77 = -1336
- db_at_76 = -1336
- db_at_75 = -1336
- db_at_74 = -1336
- db_at_73 = -1492
- db_at_72 = -1492
- db_at_71 = -1492
- db_at_70 = -1492
- db_at_69 = -1648
- db_at_68 = -1648
- db_at_67 = -1648
- db_at_66 = -1648
- db_at_65 = -1804
- db_at_64 = -1804
- db_at_63 = -1804
- db_at_62 = -1804
- db_at_61 = -1960
- db_at_60 = -1960
- db_at_59 = -1960
- db_at_58 = -2116
- db_at_57 = -2116
- db_at_56 = -2116
- db_at_55 = -2116
- db_at_54 = -2116
- db_at_53 = -2272
- db_at_52 = -2272
- db_at_51 = -2272
- db_at_50 = -2272
- db_at_49 = -2428
- db_at_48 = -2428
- db_at_47 = -2428
- db_at_46 = -2584
- db_at_45 = -2584
- db_at_44 = -2584
- db_at_43 = -2584
- db_at_42 = -2584
- db_at_41 = -2740
- db_at_40 = -2740
- db_at_39 = -2740
- db_at_38 = -2740
- db_at_37 = -2896
- db_at_36 = -2896
- db_at_35 = -2896
- db_at_34 = -3052
- db_at_33 = -3052
- db_at_32 = -3052
- db_at_31 = -3052
- db_at_30 = -3052
- db_at_29 = -3208
- db_at_28 = -3208
- db_at_27 = -3208
- db_at_26 = -3208
- db_at_25 = -3364
- db_at_24 = -3364
- db_at_23 = -3364
- db_at_22 = -3520
- db_at_21 = -3520
- db_at_20 = -3520
- db_at_19 = -3520
- db_at_18 = -3676
- db_at_17 = -3676
- db_at_16 = -3676
- db_at_15 = -3676
- db_at_14 = -3676
- db_at_13 = -3832
- db_at_12 = -3832
- db_at_11 = -3832
- db_at_10 = -3988
- db_at_9 = -3988
- db_at_8 = -3988
- db_at_7 = -3988
- db_at_6 = -3988
- db_at_5 = -4144
- db_at_4 = -4144
- db_at_3 = -4144
- db_at_2 = -4144
- db_at_1 = -4300
- db_at_0 = -4300
-[Headphone]
- volume_curve = explicit
- db_at_100 = -300
- db_at_99 = -300
- db_at_98 = -300
- db_at_97 = -540
- db_at_96 = -540
- db_at_95 = -540
- db_at_94 = -540
- db_at_93 = -780
- db_at_92 = -780
- db_at_91 = -780
- db_at_90 = -780
- db_at_89 = -1020
- db_at_88 = -1020
- db_at_87 = -1020
- db_at_86 = -1020
- db_at_85 = -1260
- db_at_84 = -1260
- db_at_83 = -1260
- db_at_82 = -1260
- db_at_81 = -1500
- db_at_80 = -1500
- db_at_79 = -1500
- db_at_78 = -1500
- db_at_77 = -1740
- db_at_76 = -1740
- db_at_75 = -1740
- db_at_74 = -1740
- db_at_73 = -1980
- db_at_72 = -1980
- db_at_71 = -1980
- db_at_70 = -1980
- db_at_69 = -2220
- db_at_68 = -2220
- db_at_67 = -2220
- db_at_66 = -2220
- db_at_65 = -2460
- db_at_64 = -2460
- db_at_63 = -2460
- db_at_62 = -2460
- db_at_61 = -2700
- db_at_60 = -2700
- db_at_59 = -2700
- db_at_58 = -2700
- db_at_57 = -2940
- db_at_56 = -2940
- db_at_55 = -2940
- db_at_54 = -2940
- db_at_53 = -3180
- db_at_52 = -3180
- db_at_51 = -3180
- db_at_50 = -3180
- db_at_49 = -3420
- db_at_48 = -3420
- db_at_47 = -3420
- db_at_46 = -3660
- db_at_45 = -3660
- db_at_44 = -3660
- db_at_43 = -3660
- db_at_42 = -3660
- db_at_41 = -3900
- db_at_40 = -3900
- db_at_39 = -3900
- db_at_38 = -3900
- db_at_37 = -4140
- db_at_36 = -4140
- db_at_35 = -4140
- db_at_34 = -4140
- db_at_33 = -4380
- db_at_32 = -4380
- db_at_31 = -4380
- db_at_30 = -4380
- db_at_29 = -4620
- db_at_28 = -4620
- db_at_27 = -4620
- db_at_26 = -4860
- db_at_25 = -4860
- db_at_24 = -4860
- db_at_23 = -4860
- db_at_22 = -4860
- db_at_21 = -5100
- db_at_20 = -5100
- db_at_19 = -5100
- db_at_18 = -5340
- db_at_17 = -5340
- db_at_16 = -5340
- db_at_15 = -5340
- db_at_14 = -5340
- db_at_13 = -5580
- db_at_12 = -5580
- db_at_11 = -5580
- db_at_10 = -5580
- db_at_9 = -5820
- db_at_8 = -5820
- db_at_7 = -5820
- db_at_6 = -6060
- db_at_5 = -6060
- db_at_4 = -6060
- db_at_3 = -6060
- db_at_2 = -6060
- db_at_1 = -6300
- db_at_0 = -6300
diff --git a/cras-config/quawks/dsp.ini b/cras-config/quawks/dsp.ini
deleted file mode 100644
index 4d388f58..00000000
--- a/cras-config/quawks/dsp.ini
+++ /dev/null
@@ -1,103 +0,0 @@
-[output_source]
-library=builtin
-label=source
-purpose=playback
-disable=(not (equal? dsp_name "speaker_eq"))
-output_0={src:0}
-output_1={src:1}
-
-[output_sink]
-library=builtin
-label=sink
-purpose=playback
-input_0={dst:0}
-input_1={dst:1}
-
-[drc]
-library=builtin
-label=drc
-input_0={src:0}
-input_1={src:1}
-output_2={intermediate:0}
-output_3={intermediate:1}
-input_4=1 ; emphasis_disabled
-input_5=0 ; f
-input_6=0 ; enable
-input_7=-24 ; threshold
-input_8=30 ; knee
-input_9=12 ; ratio
-input_10=0.003 ; attack
-input_11=0.25 ; release
-input_12=0 ; boost
-input_13=120 ; f
-input_14=1 ; enable
-input_15=-24 ; threshold
-input_16=30 ; knee
-input_17=12 ; ratio
-input_18=0.003 ; attack
-input_19=0.25 ; release
-input_20=0 ; boost
-input_21=2000 ; f
-input_22=0 ; enable
-input_23=-24 ; threshold
-input_24=30 ; knee
-input_25=12 ; ratio
-input_26=0.003 ; attack
-input_27=0.25 ; release
-input_28=0 ; boost
-
-[eq2]
-library=builtin
-label=eq2
-input_0={intermediate:0}
-input_1={intermediate:1}
-output_2={dst:0}
-output_3={dst:1}
-input_4=2 ; highpass
-input_5=120 ; freq
-input_6=0 ; Q
-input_7=0 ; gain
-input_8=2 ; highpass
-input_9=120 ; freq
-input_10=0 ; Q
-input_11=0 ; gain
-input_12=6 ; peaking
-input_13=450 ; freq
-input_14=6 ; Q
-input_15=-9 ; gain
-input_16=6 ; peaking
-input_17=450 ; freq
-input_18=6 ; Q
-input_19=-9 ; gain
-input_20=6 ; peaking
-input_21=730 ; freq
-input_22=6 ; Q
-input_23=-6 ; gain
-input_24=6 ; peaking
-input_25=730 ; freq
-input_26=6 ; Q
-input_27=-6 ; gain
-input_28=6 ; peaking
-input_29=930 ; freq
-input_30=3 ; Q
-input_31=-6 ; gain
-input_32=6 ; peaking
-input_33=930 ; freq
-input_34=3 ; Q
-input_35=-6 ; gain
-input_36=6 ; peaking
-input_37=1250 ; freq
-input_38=6 ; Q
-input_39=-6 ; gain
-input_40=6 ; peaking
-input_41=1250 ; freq
-input_42=6 ; Q
-input_43=-6 ; gain
-input_44=1 ; lowpass
-input_45=16000 ; freq
-input_46=0 ; Q
-input_47=0 ; gain
-input_48=1 ; lowpass
-input_49=16000 ; freq
-input_50=0 ; Q
-input_51=0 ; gain
diff --git a/cras-config/squawks/byt-max98090 b/cras-config/squawks/byt-max98090
deleted file mode 100644
index e4d7045d..00000000
--- a/cras-config/squawks/byt-max98090
+++ /dev/null
@@ -1,206 +0,0 @@
-[Speaker]
- volume_curve = explicit
- db_at_100 = -400
- db_at_99 = -400
- db_at_98 = -400
- db_at_97 = -592
- db_at_96 = -592
- db_at_95 = -592
- db_at_94 = -592
- db_at_93 = -784
- db_at_92 = -784
- db_at_91 = -784
- db_at_90 = -784
- db_at_89 = -976
- db_at_88 = -976
- db_at_87 = -976
- db_at_86 = -976
- db_at_85 = -1168
- db_at_84 = -1168
- db_at_83 = -1168
- db_at_82 = -1168
- db_at_81 = -1360
- db_at_80 = -1360
- db_at_79 = -1360
- db_at_78 = -1360
- db_at_77 = -1552
- db_at_76 = -1552
- db_at_75 = -1552
- db_at_74 = -1552
- db_at_73 = -1744
- db_at_72 = -1744
- db_at_71 = -1744
- db_at_70 = -1744
- db_at_69 = -1936
- db_at_68 = -1936
- db_at_67 = -1936
- db_at_66 = -1936
- db_at_65 = -2128
- db_at_64 = -2128
- db_at_63 = -2128
- db_at_62 = -2128
- db_at_61 = -2320
- db_at_60 = -2320
- db_at_59 = -2320
- db_at_58 = -2320
- db_at_57 = -2512
- db_at_56 = -2512
- db_at_55 = -2512
- db_at_54 = -2512
- db_at_53 = -2704
- db_at_52 = -2704
- db_at_51 = -2704
- db_at_50 = -2704
- db_at_49 = -2896
- db_at_48 = -2896
- db_at_47 = -2896
- db_at_46 = -2896
- db_at_45 = -3088
- db_at_44 = -3088
- db_at_43 = -3088
- db_at_42 = -3088
- db_at_41 = -3280
- db_at_40 = -3280
- db_at_39 = -3280
- db_at_38 = -3280
- db_at_37 = -3472
- db_at_36 = -3472
- db_at_35 = -3472
- db_at_34 = -3472
- db_at_33 = -3664
- db_at_32 = -3664
- db_at_31 = -3664
- db_at_30 = -3856
- db_at_29 = -3856
- db_at_28 = -3856
- db_at_27 = -3856
- db_at_26 = -3856
- db_at_25 = -4048
- db_at_24 = -4048
- db_at_23 = -4048
- db_at_22 = -4048
- db_at_21 = -4240
- db_at_20 = -4240
- db_at_19 = -4240
- db_at_18 = -4240
- db_at_17 = -4432
- db_at_16 = -4432
- db_at_15 = -4432
- db_at_14 = -4624
- db_at_13 = -4624
- db_at_12 = -4624
- db_at_11 = -4624
- db_at_10 = -4816
- db_at_9 = -4816
- db_at_8 = -4816
- db_at_7 = -4816
- db_at_6 = -4816
- db_at_5 = -5008
- db_at_4 = -5008
- db_at_3 = -5008
- db_at_2 = -5008
- db_at_1 = -5200
- db_at_0 = -5200
-[Headphone]
- volume_curve = explicit
- db_at_100 = -300
- db_at_99 = -300
- db_at_98 = -300
- db_at_97 = -540
- db_at_96 = -540
- db_at_95 = -540
- db_at_94 = -540
- db_at_93 = -780
- db_at_92 = -780
- db_at_91 = -780
- db_at_90 = -780
- db_at_89 = -1020
- db_at_88 = -1020
- db_at_87 = -1020
- db_at_86 = -1020
- db_at_85 = -1260
- db_at_84 = -1260
- db_at_83 = -1260
- db_at_82 = -1260
- db_at_81 = -1500
- db_at_80 = -1500
- db_at_79 = -1500
- db_at_78 = -1500
- db_at_77 = -1740
- db_at_76 = -1740
- db_at_75 = -1740
- db_at_74 = -1740
- db_at_73 = -1980
- db_at_72 = -1980
- db_at_71 = -1980
- db_at_70 = -1980
- db_at_69 = -2220
- db_at_68 = -2220
- db_at_67 = -2220
- db_at_66 = -2220
- db_at_65 = -2460
- db_at_64 = -2460
- db_at_63 = -2460
- db_at_62 = -2460
- db_at_61 = -2700
- db_at_60 = -2700
- db_at_59 = -2700
- db_at_58 = -2700
- db_at_57 = -2940
- db_at_56 = -2940
- db_at_55 = -2940
- db_at_54 = -2940
- db_at_53 = -3180
- db_at_52 = -3180
- db_at_51 = -3180
- db_at_50 = -3180
- db_at_49 = -3420
- db_at_48 = -3420
- db_at_47 = -3420
- db_at_46 = -3660
- db_at_45 = -3660
- db_at_44 = -3660
- db_at_43 = -3660
- db_at_42 = -3660
- db_at_41 = -3900
- db_at_40 = -3900
- db_at_39 = -3900
- db_at_38 = -3900
- db_at_37 = -4140
- db_at_36 = -4140
- db_at_35 = -4140
- db_at_34 = -4140
- db_at_33 = -4380
- db_at_32 = -4380
- db_at_31 = -4380
- db_at_30 = -4380
- db_at_29 = -4620
- db_at_28 = -4620
- db_at_27 = -4620
- db_at_26 = -4860
- db_at_25 = -4860
- db_at_24 = -4860
- db_at_23 = -4860
- db_at_22 = -4860
- db_at_21 = -5100
- db_at_20 = -5100
- db_at_19 = -5100
- db_at_18 = -5340
- db_at_17 = -5340
- db_at_16 = -5340
- db_at_15 = -5340
- db_at_14 = -5340
- db_at_13 = -5580
- db_at_12 = -5580
- db_at_11 = -5580
- db_at_10 = -5580
- db_at_9 = -5820
- db_at_8 = -5820
- db_at_7 = -5820
- db_at_6 = -6060
- db_at_5 = -6060
- db_at_4 = -6060
- db_at_3 = -6060
- db_at_2 = -6060
- db_at_1 = -6300
- db_at_0 = -6300
diff --git a/cras-config/squawks/dsp.ini b/cras-config/squawks/dsp.ini
deleted file mode 100644
index c32c80ae..00000000
--- a/cras-config/squawks/dsp.ini
+++ /dev/null
@@ -1,103 +0,0 @@
-[output_source]
-library=builtin
-label=source
-purpose=playback
-disable=(not (equal? dsp_name "speaker_eq"))
-output_0={src:0}
-output_1={src:1}
-
-[output_sink]
-library=builtin
-label=sink
-purpose=playback
-input_0={dst:0}
-input_1={dst:1}
-
-[drc]
-library=builtin
-label=drc
-input_0={src:0}
-input_1={src:1}
-output_2={intermediate:0}
-output_3={intermediate:1}
-input_4=1 ; emphasis_disabled
-input_5=0 ; f
-input_6=0 ; enable
-input_7=-24 ; threshold
-input_8=30 ; knee
-input_9=12 ; ratio
-input_10=0.003 ; attack
-input_11=0.25 ; release
-input_12=0 ; boost
-input_13=160 ; f
-input_14=1 ; enable
-input_15=-24 ; threshold
-input_16=30 ; knee
-input_17=12 ; ratio
-input_18=0.003 ; attack
-input_19=0.25 ; release
-input_20=0 ; boost
-input_21=2000 ; f
-input_22=0 ; enable
-input_23=-24 ; threshold
-input_24=30 ; knee
-input_25=12 ; ratio
-input_26=0.003 ; attack
-input_27=0.25 ; release
-input_28=0 ; boost
-
-[eq2]
-library=builtin
-label=eq2
-input_0={intermediate:0}
-input_1={intermediate:1}
-output_2={dst:0}
-output_3={dst:1}
-input_4=2 ; highpass
-input_5=200 ; freq
-input_6=0 ; Q
-input_7=0 ; gain
-input_8=2 ; highpass
-input_9=200 ; freq
-input_10=0 ; Q
-input_11=0 ; gain
-input_12=2 ; highpass
-input_13=200 ; freq
-input_14=0 ; Q
-input_15=-9 ; gain
-input_16=2 ; highpass
-input_17=200 ; freq
-input_18=0 ; Q
-input_19=-6 ; gain
-input_20=6 ; peaking
-input_21=520 ; freq
-input_22=6 ; Q
-input_23=-15 ; gain
-input_24=6 ; peaking
-input_25=550 ; freq
-input_26=6 ; Q
-input_27=-15 ; gain
-input_28=6 ; peaking
-input_29=1100 ; freq
-input_30=6 ; Q
-input_31=-12 ; gain
-input_32=6 ; peaking
-input_33=1100 ; freq
-input_34=6 ; Q
-input_35=-12 ; gain
-input_36=6 ; peaking
-input_37=770 ; freq
-input_38=3 ; Q
-input_39=-9 ; gain
-input_40=6 ; peaking
-input_41=770 ; freq
-input_42=3 ; Q
-input_43=-9 ; gain
-input_44=1 ; lowpass
-input_45=16000 ; freq
-input_46=0 ; Q
-input_47=0 ; gain
-input_48=1 ; lowpass
-input_49=16000 ; freq
-input_50=0 ; Q
-input_51=0 ; gain
diff --git a/cras-config/winky/byt-max98090 b/cras-config/winky/byt-max98090
deleted file mode 100644
index 821cdb3a..00000000
--- a/cras-config/winky/byt-max98090
+++ /dev/null
@@ -1,206 +0,0 @@
-[Speaker]
- volume_curve = explicit
- db_at_100 = -500
- db_at_99 = -500
- db_at_98 = -500
- db_at_97 = -500
- db_at_96 = -600
- db_at_95 = -600
- db_at_94 = -600
- db_at_93 = -600
- db_at_92 = -700
- db_at_91 = -700
- db_at_90 = -700
- db_at_89 = -700
- db_at_88 = -800
- db_at_87 = -800
- db_at_86 = -800
- db_at_85 = -800
- db_at_84 = -900
- db_at_83 = -900
- db_at_82 = -900
- db_at_81 = -900
- db_at_80 = -1000
- db_at_79 = -1000
- db_at_78 = -1000
- db_at_77 = -1000
- db_at_76 = -1100
- db_at_75 = -1100
- db_at_74 = -1100
- db_at_73 = -1100
- db_at_72 = -1200
- db_at_71 = -1200
- db_at_70 = -1200
- db_at_69 = -1200
- db_at_68 = -1300
- db_at_67 = -1300
- db_at_66 = -1300
- db_at_65 = -1300
- db_at_64 = -1400
- db_at_63 = -1400
- db_at_62 = -1400
- db_at_61 = -1400
- db_at_60 = -1500
- db_at_59 = -1500
- db_at_58 = -1500
- db_at_57 = -1500
- db_at_56 = -1600
- db_at_55 = -1600
- db_at_54 = -1600
- db_at_53 = -1600
- db_at_52 = -1700
- db_at_51 = -1700
- db_at_50 = -1700
- db_at_49 = -1700
- db_at_48 = -1800
- db_at_47 = -1800
- db_at_46 = -1800
- db_at_45 = -1800
- db_at_44 = -1900
- db_at_43 = -1900
- db_at_42 = -1900
- db_at_41 = -1900
- db_at_40 = -2100
- db_at_39 = -2100
- db_at_38 = -2100
- db_at_37 = -2100
- db_at_36 = -2300
- db_at_35 = -2300
- db_at_34 = -2300
- db_at_33 = -2300
- db_at_32 = -2500
- db_at_31 = -2500
- db_at_30 = -2500
- db_at_29 = -2500
- db_at_28 = -2700
- db_at_27 = -2700
- db_at_26 = -2700
- db_at_25 = -2700
- db_at_24 = -3000
- db_at_23 = -3000
- db_at_22 = -3000
- db_at_21 = -3000
- db_at_20 = -3300
- db_at_19 = -3300
- db_at_18 = -3300
- db_at_17 = -3300
- db_at_16 = -3700
- db_at_15 = -3700
- db_at_14 = -3700
- db_at_13 = -3700
- db_at_12 = -4100
- db_at_11 = -4100
- db_at_10 = -4100
- db_at_9 = -4100
- db_at_8 = -4700
- db_at_7 = -4700
- db_at_6 = -4700
- db_at_5 = -4700
- db_at_4 = -5400
- db_at_3 = -5400
- db_at_2 = -5400
- db_at_1 = -5400
- db_at_0 = -6500
-[Headphone]
- volume_curve = explicit
- db_at_100 = -900
- db_at_99 = -900
- db_at_98 = -900
- db_at_97 = -900
- db_at_96 = -1000
- db_at_95 = -1000
- db_at_94 = -1000
- db_at_93 = -1000
- db_at_92 = -1200
- db_at_91 = -1200
- db_at_90 = -1200
- db_at_89 = -1200
- db_at_88 = -1400
- db_at_87 = -1400
- db_at_86 = -1400
- db_at_85 = -1400
- db_at_84 = -1600
- db_at_83 = -1600
- db_at_82 = -1600
- db_at_81 = -1600
- db_at_80 = -1800
- db_at_79 = -1800
- db_at_78 = -1800
- db_at_77 = -1800
- db_at_76 = -2000
- db_at_75 = -2000
- db_at_74 = -2000
- db_at_73 = -2000
- db_at_72 = -2200
- db_at_71 = -2200
- db_at_70 = -2200
- db_at_69 = -2200
- db_at_68 = -2300
- db_at_67 = -2300
- db_at_66 = -2300
- db_at_65 = -2300
- db_at_64 = -2500
- db_at_63 = -2500
- db_at_62 = -2500
- db_at_61 = -2500
- db_at_60 = -2700
- db_at_59 = -2700
- db_at_58 = -2700
- db_at_57 = -2700
- db_at_56 = -2900
- db_at_55 = -2900
- db_at_54 = -2900
- db_at_53 = -2900
- db_at_52 = -3100
- db_at_51 = -3100
- db_at_50 = -3100
- db_at_49 = -3100
- db_at_48 = -3300
- db_at_47 = -3300
- db_at_46 = -3300
- db_at_45 = -3300
- db_at_44 = -3500
- db_at_43 = -3500
- db_at_42 = -3500
- db_at_41 = -3500
- db_at_40 = -3700
- db_at_39 = -3700
- db_at_38 = -3700
- db_at_37 = -3700
- db_at_36 = -4000
- db_at_35 = -4000
- db_at_34 = -4000
- db_at_33 = -4000
- db_at_32 = -4300
- db_at_31 = -4300
- db_at_30 = -4300
- db_at_29 = -4300
- db_at_28 = -4600
- db_at_27 = -4600
- db_at_26 = -4600
- db_at_25 = -4600
- db_at_24 = -4900
- db_at_23 = -4900
- db_at_22 = -4900
- db_at_21 = -4900
- db_at_20 = -5200
- db_at_19 = -5200
- db_at_18 = -5200
- db_at_17 = -5200
- db_at_16 = -5500
- db_at_15 = -5500
- db_at_14 = -5500
- db_at_13 = -5500
- db_at_12 = -5800
- db_at_11 = -5800
- db_at_10 = -5800
- db_at_9 = -5800
- db_at_8 = -6200
- db_at_7 = -6200
- db_at_6 = -6200
- db_at_5 = -6200
- db_at_4 = -6600
- db_at_3 = -6600
- db_at_2 = -6600
- db_at_1 = -6600
- db_at_0 = -7000
diff --git a/cras-config/winky/dsp.ini b/cras-config/winky/dsp.ini
deleted file mode 100644
index 2055b1fd..00000000
--- a/cras-config/winky/dsp.ini
+++ /dev/null
@@ -1,111 +0,0 @@
-[output_source]
-library=builtin
-label=source
-purpose=playback
-disable=(not (equal? dsp_name "speaker_eq"))
-output_0={src:0}
-output_1={src:1}
-
-[output_sink]
-library=builtin
-label=sink
-purpose=playback
-input_0={dst:0}
-input_1={dst:1}
-
-[eq2]
-library=builtin
-label=eq2
-input_0={src:0}
-input_1={src:1}
-output_2={intermediate:0}
-output_3={intermediate:1}
-input_4=2 ; highpass
-input_5=200 ; freq
-input_6=0 ; Q
-input_7=-8 ; gain
-input_8=2 ; highpass
-input_9=200 ; freq
-input_10=0 ; Q
-input_11=-8 ; gain
-input_12=5 ; highshelf
-input_13=20 ; freq
-input_14=0 ; Q
-input_15=-8 ; gain
-input_16=5 ; highshelf
-input_17=20 ; freq
-input_18=0 ; Q
-input_19=-8 ; gain
-input_20=6 ; peaking
-input_21=400 ; freq
-input_22=2 ; Q
-input_23=4 ; gain
-input_24=6 ; peaking
-input_25=400 ; freq
-input_26=2 ; Q
-input_27=4 ; gain
-input_28=6 ; peaking
-input_29=850 ; freq
-input_30=2.5 ; Q
-input_31=-5.2 ; gain
-input_32=6 ; peaking
-input_33=850 ; freq
-input_34=2.5 ; Q
-input_35=-5.2 ; gain
-input_36=6 ; peaking
-input_37=2400 ; freq
-input_38=1.2 ; Q
-input_39=2 ; gain
-input_40=6 ; peaking
-input_41=2400 ; freq
-input_42=1.2 ; Q
-input_43=2 ; gain
-input_44=6 ; peaking
-input_45=5250 ; freq
-input_46=2 ; Q
-input_47=-11 ; gain
-input_48=6 ; peaking
-input_49=5250 ; freq
-input_50=2 ; Q
-input_51=-11 ; gain
-input_52=6 ; peaking
-input_53=9000 ; freq
-input_54=1 ; Q
-input_55=7.5 ; gain
-input_56=6 ; peaking
-input_57=9000 ; freq
-input_58=1 ; Q
-input_59=7.5 ; gain
-
-[drc]
-library=builtin
-label=drc
-input_0={intermediate:0}
-input_1={intermediate:1}
-output_2={dst:0}
-output_3={dst:1}
-input_4=1 ; emphasis_disabled
-input_5=0 ; f
-input_6=1 ; enable
-input_7=-11 ; threshold
-input_8=0 ; knee
-input_9=999 ; ratio
-input_10=0.001 ; attack
-input_11=0.8 ; release
-input_12=4.4 ; boost
-input_13=20000 ; f
-input_14=0 ; enable
-input_15=-11 ; threshold
-input_16=0 ; knee
-input_17=999 ; ratio
-input_18=0.1 ; attack
-input_19=0.8 ; release
-input_20=4.7 ; boost
-input_21=21000 ; f
-input_22=0 ; enable
-input_23=-11 ; threshold
-input_24=0 ; knee
-input_25=999 ; ratio
-input_26=0.1 ; attack
-input_27=0.8 ; release
-input_28=4.7 ; boost
diff --git a/cras/.gitignore b/cras/.gitignore
index ea2c721c..40d4c800 100644
--- a/cras/.gitignore
+++ b/cras/.gitignore
@@ -7,11 +7,15 @@ src/libcras.so*
tags
libcras.pc
src/mix_unittest
+src/cras_monitor
src/cras_test_client
src/*_unittest
+/.__autoconf_trace_data
+/.elibtoolized
/Makefile
/Makefile.in
/aclocal.m4
+/ar-lib
autom4te.cache/
/compile
/config.guess
@@ -24,6 +28,10 @@ autom4te.cache/
/libtool
/ltmain.sh
/missing
+/test-driver
+src/*.la
+src/*.log
+src/*.trs
src/.libs/
src/Makefile
src/Makefile.in
@@ -52,5 +60,6 @@ src/dsp/.deps/
src/dsp/.dirstamp
src/dsp/tests/.deps/
src/dsp/tests/.dirstamp
+src/dsp_util_test
src/eq2_test
src/eq_test
diff --git a/cras/README b/cras/README
index 58ad0887..bb7004a9 100644
--- a/cras/README
+++ b/cras/README
@@ -48,7 +48,19 @@ has the following format.
[<output-node-name>] ; Name of the mixer control for this output.
<config-option> = <config-value>
-output-node-name is the mixer control name, e.g. "Headphone" or "Speaker".
+output-node-name can be speficied in a few ways to link with the real node:
+ UCM device name - The name string following the SectionDevice label in UCM
+ config, i.e. HiFi.conf
+ Jack name - Name of the mixer control for mixer jack, or the gpio jack name
+ listed by 'evtest' command.
+ Mixer control name - e.g. "Headphone" or "Speaker", listed by
+ 'amixer scontrols' command.
+
+Note that an output node matches to the output-node-name label in card config by
+priorty ordered above. For example if a node has UCM device, it will first
+search the config file for the UCM device name. When not found, jack name will
+be used for searching, and lastly the mixer output control name.
+
config-option can be the following:
volume_curve - The type of volume curve, "simple_step" or "explicit".
Options valid and mandatory when volume_curve = simple_step:
diff --git a/cras/README.dbus-api b/cras/README.dbus-api
index daa480ab..819f068b 100644
--- a/cras/README.dbus-api
+++ b/cras/README.dbus-api
@@ -96,6 +96,10 @@ Methods void SetOutputVolume(int32 volume)
uint64 StableDeviceId
The stable ID does not change due to
device plug/unplug or reboot.
+ uint64 StableDeviceIdNew
+ The new stable ID. Keeping both stable
+ ID and stable ID new is for backward
+ compatibility.
boolean Active
Whether this node is currently used
for output/input. There is one active
@@ -107,6 +111,12 @@ Methods void SetOutputVolume(int32 volume)
string MicPositions
The string formed by floating numbers
describing the position of mic array.
+ string HotwordModels
+ A string of comma-separated hotword
+ language model locales supported by this
+ node. e.g. "en_au,en_gb,en_us"
+ The string is empty if the node type is
+ not HOTWORD.
void SetActiveOutputNode(uint64 node_id);
@@ -135,6 +145,28 @@ Methods void SetOutputVolume(int32 volume)
Returns the number of streams currently using output hardware.
+ void SetGlobalOutputChannelRemix(int32 num_channels,
+ array:double coefficient)
+
+ Sets the conversion matrix for global output channel
+ remixing. The coefficient array represents an N * N
+ conversion matrix M, where N is num_channels, with
+ M[i][j] = coefficient[i * N + j].
+ The remix is done by multiplying the conversion matrix
+ to each N-channel PCM data, i.e M * [L, R] = [L', R']
+ For example, coefficient [0.1, 0.9, 0.4, 0.6] will
+ result in:
+ L' = 0.1 * L + 0.9 * R
+ R' = 0.4 * L + 0.6 * R
+
+ int32 SetHotwordModel(uint64_t node_id, string model_name)
+
+ Set the hotword language model on the specified node.
+ The node must have type HOTWORD and the model_name must
+ be one of the supported locales returned by
+ GetNodes() HotwordModels string.
+ Returns 0 on success, or a negative errno on failure.
+
Signals OutputVolumeChanged(int32 volume)
Indicates that the output volume level has changed.
diff --git a/cras/configure.ac b/cras/configure.ac
index d13715d7..97d08494 100644
--- a/cras/configure.ac
+++ b/cras/configure.ac
@@ -4,8 +4,13 @@
AC_INIT([cras], [0.1], [dgreid@chromium.org],
[cras], [http://www.chromium.org/])
AC_PREREQ([2.59])
+
+AC_CANONICAL_TARGET
+
AM_INIT_AUTOMAKE([1.10 -Wall no-define])
#AC_CONFIG_HEADERS([config.h])
+
+m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
AC_PROG_LIBTOOL
AC_PROG_CC
# c++ unit test (gtest).
@@ -16,8 +21,21 @@ PKG_PROG_PKG_CONFIG
#AC_CONFIG_FILES([Makefile src/Makefile libcras.pc])
PKG_CHECK_MODULES([LIBSPEEX], [ speexdsp >= 1.2 ])
-PKG_CHECK_MODULES([ASOUNDLIB], [ alsa >= 1.0.27 ])
-PKG_CHECK_MODULES([DBUS], [ dbus-1 >= 1.4.12 ])
+PKG_CHECK_MODULES([ASOUNDLIB], [ alsa >= 1.1.0 ])
+
+AC_ARG_ENABLE([DBUS], AS_HELP_STRING([--disable-DBUS], [Disable all DBUS uses]), have_dbus=$enableval, have_dbus=yes)
+AM_CONDITIONAL(HAVE_DBUS, test "$have_dbus" = "yes")
+if test "$have_dbus" = "yes"; then
+ PKG_CHECK_MODULES([DBUS], [ dbus-1 >= 1.4.12 ])
+ DBUS_CFLAGS+=-DCRAS_DBUS
+ AC_SUBST(DBUS_CFLAGS)
+else
+ DBUS_CFLAGS=
+ AC_SUBST(DBUS_CFLAGS)
+ DBUS_LIBS=
+ AC_SUBST(DBUS_LIBS)
+fi
+
PKG_CHECK_MODULES([SBC], [ sbc >= 1.0 ])
AC_CHECK_LIB(asound, snd_pcm_ioplug_create,,
AC_ERROR([*** libasound has no external plugin SDK]), -ldl)
@@ -46,6 +64,20 @@ AC_DEFINE_UNQUOTED(ALSA_PLUGIN_DIR, "$plugindir",
ALSA_PLUGIN_DIR="$plugindir"
AC_SUBST(ALSA_PLUGIN_DIR)
+# Determine CRAS configuration directory.
+eval cras_config_file_dir="$sysconfdir/cras"
+AC_DEFINE_UNQUOTED(CRAS_CONFIG_FILE_DIR, "$cras_config_file_dir",
+ [directory containing CRAS configuration])
+
+# CRAS socket dir
+AC_ARG_WITH(socketdir,
+ AS_HELP_STRING([--with-socketdir=dir],
+ [path where CRAS stores its sockets]),
+ socketdir="$withval",
+ socketdir="/run/cras")
+AC_DEFINE_UNQUOTED(CRAS_SOCKET_FILE_DIR, "$socketdir",
+ [directory containing CRAS socket files])
+
# Get iniparser library and include locations
AC_ARG_WITH([iniparser-include-path],
[AS_HELP_STRING([--with-iniparser-include-path],
@@ -61,8 +93,68 @@ AC_ARG_WITH([iniparser-lib-path],
[INIPARSER_LIBS='-liniparser'])
AC_SUBST([INIPARSER_LIBS])
+# SSE4_2 support
+AC_ARG_ENABLE(sse42, [AS_HELP_STRING([--enable-sse42],[enable SSE42 optimizations])], have_sse42=$enableval, have_sse42=yes)
+if test "x$target_cpu" != xx86_64; then
+ have_sse42=no
+fi
+if test "$have_sse42" = "yes"; then
+ AC_DEFINE(HAVE_SSE42,1,[Define to enable SSE42 optimizations.])
+ SSE42_CFLAGS="-DOPS_SSE42 -msse4.2 -ffast-math"
+fi
+AM_CONDITIONAL(HAVE_SSE42, test "$have_sse42" = "yes")
+AC_SUBST(SSE42_CFLAGS)
+
+# AVX support
+AC_ARG_ENABLE(avx, [AS_HELP_STRING([--enable-avx],[enable AVX optimizations])], have_avx=$enableval, have_avx=yes)
+if test "x$target_cpu" != xx86_64; then
+ have_avx=no
+fi
+if test "$have_avx" = "yes"; then
+ AC_DEFINE(HAVE_AVX,1,[Define to enable AVX optimizations.])
+ AVX_CFLAGS="-DOPS_AVX -mavx -ffast-math"
+fi
+AM_CONDITIONAL(HAVE_AVX, test "$have_avx" = "yes")
+AC_SUBST(AVX_CFLAGS)
+
+# AVX2 support
+AC_ARG_ENABLE(avx2, [AS_HELP_STRING([--enable-avx2],[enable AVX2 optimizations])], have_avx2=$enableval, have_avx2=yes)
+if test "x$target_cpu" != xx86_64; then
+ have_avx2=no
+fi
+if test "$have_avx2" = "yes"; then
+ AC_DEFINE(HAVE_AVX2,1,[Define to enable AVX2 optimizations.])
+ AVX2_CFLAGS="-DOPS_AVX2 -mavx2 -ffast-math"
+fi
+AM_CONDITIONAL(HAVE_AVX2, test "$have_avx2" = "yes")
+AC_SUBST(AVX2_CFLAGS)
+
+# FMA support
+AC_ARG_ENABLE(fma, [AS_HELP_STRING([--enable-fma],[enable FMA optimizations])], have_fma=$enableval, have_fma=yes)
+if test "x$target_cpu" != xx86_64; then
+ have_fma=no
+fi
+if test "$have_fma" = "yes"; then
+ AC_DEFINE(HAVE_FMA,1,[Define to enable FMA optimizations.])
+ FMA_CFLAGS="-DOPS_FMA -mavx2 -mfma -ffast-math"
+fi
+AM_CONDITIONAL(HAVE_FMA, test "$have_fma" = "yes")
+AC_SUBST(FMA_CFLAGS)
+
AC_OUTPUT([
Makefile
src/Makefile
libcras.pc
])
+
+AS_IF([test "$have_sse42" = "yes"], ENABLE_SSE42=yes, ENABLE_SSE42=no)
+AS_IF([test "$have_avx" = "yes"], ENABLE_AVX=yes, ENABLE_AVX=no)
+AS_IF([test "$have_avx2" = "yes"], ENABLE_AVX2=yes, ENABLE_AVX2=no)
+AS_IF([test "$have_fma" = "yes"], ENABLE_FMA=yes, ENABLE_FMA=no)
+
+echo "
+Enable SSE42: ${ENABLE_SSE42}
+Enable AVX: ${ENABLE_AVX}
+Enable AVX2: ${ENABLE_AVX2}
+Enable FMA: ${ENABLE_FMA}
+"
diff --git a/cras/src/Android.mk b/cras/src/Android.mk
index 63d6110e..fc2ee4c0 100644
--- a/cras/src/Android.mk
+++ b/cras/src/Android.mk
@@ -5,13 +5,12 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
common/cras_audio_format.c \
common/cras_config.c \
+ common/cras_file_wait.c \
common/cras_util.c \
common/edid_utils.c \
libcras/cras_client.c \
libcras/cras_helpers.c
-LOCAL_SRC_FILES_arm := arm/shm.S
-
LOCAL_SHARED_LIBRARIES := \
libtinyalsa
@@ -20,6 +19,9 @@ LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/libcras \
external/tinyalsa/include
+LOCAL_CFLAGS += \
+ -DCRAS_SOCKET_FILE_DIR=\"/var/run/cras\"
+
LOCAL_MODULE := libcras
include $(BUILD_STATIC_LIBRARY)
diff --git a/cras/src/Makefile.am b/cras/src/Makefile.am
index b71d6672..e83e9481 100644
--- a/cras/src/Makefile.am
+++ b/cras/src/Makefile.am
@@ -5,14 +5,69 @@
AUTOMAKE_OPTIONS = subdir-objects
ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS}
+if HAVE_SSE42
+CRAS_SSE4_2 = libcrasmix_sse42.la
+else
+CRAS_SSE4_2 =
+endif
+
+if HAVE_AVX
+CRAS_AVX = libcrasmix_avx.la
+else
+CRAS_AVX =
+endif
+
+if HAVE_AVX2
+CRAS_AVX2 = libcrasmix_avx2.la
+else
+CRAS_AVX2 =
+endif
+
+if HAVE_FMA
+CRAS_FMA = libcrasmix_fma.la
+else
+CRAS_FMA =
+endif
+
+CRAS_UT_TMPDIR_CFLAGS=-DCRAS_UT_TMPDIR=\"/tmp\"
COMMON_CPPFLAGS = -O2 -Wall -Werror -Wno-error=cpp
+COMMON_SIMD_CPPFLAGS = -O3 -Wall -Werror -Wno-error=cpp
+
+bin_PROGRAMS = cras cras_test_client cras_monitor cras_router
+
+if HAVE_DBUS
+CRAS_DBUS_SOURCES = \
+ common/cras_sbc_codec.c \
+ server/cras_bt_manager.c \
+ server/cras_bt_adapter.c \
+ server/cras_bt_device.c \
+ server/cras_bt_transport.c \
+ server/cras_bt_endpoint.c \
+ server/cras_bt_player.c \
+ server/cras_bt_io.c \
+ server/cras_bt_profile.c \
+ server/cras_dbus.c \
+ server/cras_dbus_util.c \
+ server/cras_dbus_control.c \
+ server/cras_hfp_ag_profile.c \
+ server/cras_hfp_iodev.c \
+ server/cras_hfp_info.c \
+ server/cras_hfp_slc.c \
+ server/cras_a2dp_endpoint.c \
+ server/cras_a2dp_info.c \
+ server/cras_a2dp_iodev.c \
+ server/cras_telephony.c
+else
+CRAS_DBUS_SOURCES =
+endif
-bin_PROGRAMS = cras cras_test_client
cras_SOURCES = \
+ $(CRAS_DBUS_SOURCES) \
common/cras_audio_format.c \
common/cras_checksum.c \
common/cras_config.c \
common/cras_metrics.c \
+ common/cras_shm.c \
common/cras_util.c \
common/dumper.c \
common/edid_utils.c \
@@ -27,30 +82,21 @@ cras_SOURCES = \
dsp/eq.c \
dsp/eq2.c \
server/audio_thread.c \
+ server/buffer_share.c \
server/config/cras_card_config.c \
server/config/cras_device_blacklist.c \
server/cras.c \
- server/cras_a2dp_endpoint.c \
- server/cras_a2dp_info.c \
- server/cras_a2dp_iodev.c \
server/cras_alert.c \
server/cras_alsa_card.c \
server/cras_alsa_helpers.c \
server/cras_alsa_io.c \
server/cras_alsa_jack.c \
server/cras_alsa_mixer.c \
+ server/cras_alsa_mixer_name.c \
server/cras_alsa_ucm.c \
+ server/cras_alsa_ucm_section.c \
server/cras_audio_area.c \
- server/cras_bt_manager.c \
- server/cras_bt_adapter.c \
- server/cras_bt_device.c \
- server/cras_bt_transport.c \
- server/cras_bt_endpoint.c \
- server/cras_bt_io.c \
- server/cras_bt_profile.c \
- server/cras_dbus.c \
- server/cras_dbus_util.c \
- server/cras_dbus_control.c \
+ server/cras_device_monitor.c \
server/cras_dsp.c \
server/cras_dsp_ini.c \
server/cras_dsp_mod_builtin.c \
@@ -60,23 +106,19 @@ cras_SOURCES = \
server/cras_expr.c \
server/cras_fmt_conv.c \
server/cras_gpio_jack.c \
- server/cras_hfp_ag_profile.c \
- server/cras_hfp_info.c \
- server/cras_hfp_iodev.c \
- server/cras_hfp_slc.c \
server/cras_iodev.c \
server/cras_iodev_list.c \
server/cras_loopback_iodev.c \
server/cras_main_message.c \
server/cras_mix.c \
+ server/cras_observer.c \
+ server/cras_ramp.c \
server/cras_rclient.c \
server/cras_rstream.c \
- server/buffer_share.c \
- common/cras_sbc_codec.c \
+ server/cras_utf8.c \
server/cras_server.c \
server/cras_server_metrics.c \
server/cras_system_state.c \
- server/cras_telephony.c \
server/cras_tm.c \
server/cras_udev.c \
server/cras_volume_curve.c \
@@ -91,14 +133,74 @@ cras_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/dsp -I$(top_srcdir)/src/server \
-I$(top_srcdir)/src/server/config \
$(DBUS_CFLAGS) $(SBC_CFLAGS)
-cras_LDADD = -lpthread -lasound -lrt -liniparser -ludev -ldl -lm -lspeexdsp \
+
+cras_LDADD = \
+ libcrasmix.la \
+ $(CRAS_SSE4_2) \
+ $(CRAS_AVX) \
+ $(CRAS_AVX2) \
+ $(CRAS_FMA) \
+ -lpthread -lasound -lrt -liniparser -ludev -ldl -lm -lspeexdsp \
$(SBC_LIBS) \
$(DBUS_LIBS)
+noinst_LTLIBRARIES = \
+ $(CRAS_SSE4_2) \
+ $(CRAS_AVX) \
+ $(CRAS_AVX2) \
+ $(CRAS_FMA) \
+ libcrasmix.la
+
+libcrasmix_la_SOURCES = \
+ server/cras_mix_ops.c
+
+libcrasmix_la_CFLAGS = \
+ $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/dsp -I$(top_srcdir)/src/server \
+ -I$(top_srcdir)/src/server/config \
+ $(DBUS_CFLAGS) $(SBC_CFLAGS)
+
+libcrasmix_sse42_la_SOURCES = \
+ server/cras_mix_ops.c
+
+libcrasmix_sse42_la_CFLAGS = \
+ $(COMMON_SIMD_CPPFLAGS) -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/dsp -I$(top_srcdir)/src/server \
+ -I$(top_srcdir)/src/server/config \
+ $(DBUS_CFLAGS) $(SSE42_CFLAGS)
+
+libcrasmix_avx_la_SOURCES = \
+ server/cras_mix_ops.c
+
+libcrasmix_avx_la_CFLAGS = \
+ $(COMMON_SIMD_CPPFLAGS) -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/dsp -I$(top_srcdir)/src/server \
+ -I$(top_srcdir)/src/server/config \
+ $(DBUS_CFLAGS) $(AVX_CFLAGS)
+
+libcrasmix_avx2_la_SOURCES = \
+ server/cras_mix_ops.c
+
+libcrasmix_avx2_la_CFLAGS = \
+ $(COMMON_SIMD_CPPFLAGS) -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/dsp -I$(top_srcdir)/src/server \
+ -I$(top_srcdir)/src/server/config \
+ $(DBUS_CFLAGS) $(AVX2_CFLAGS)
+
+libcrasmix_fma_la_SOURCES = \
+ server/cras_mix_ops.c
+
+libcrasmix_fma_la_CFLAGS = \
+ $(COMMON_SIMD_CPPFLAGS) -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/dsp -I$(top_srcdir)/src/server \
+ -I$(top_srcdir)/src/server/config \
+ $(DBUS_CFLAGS) $(FMA_CFLAGS)
+
lib_LTLIBRARIES = libcras.la
libcras_la_SOURCES = \
common/cras_audio_format.c \
common/cras_config.c \
+ common/cras_file_wait.c \
common/cras_util.c \
common/edid_utils.c \
libcras/cras_client.c \
@@ -150,27 +252,37 @@ hide_install=install
$(hide_install)-asound_module_pcm_crasLTLIBRARIES: install-libLTLIBRARIES
$(hide_install)-asound_module_ctl_crasLTLIBRARIES: install-libLTLIBRARIES
+if HAVE_DBUS
+DBUS_TESTS = \
+ a2dp_info_unittest \
+ a2dp_iodev_unittest \
+ alsa_io_unittest \
+ bt_device_unittest \
+ bt_io_unittest \
+ hfp_iodev_unittest \
+ hfp_slc_unittest
+else
+DBUS_TESTS =
+endif
+
TESTS = \
+ $(DBUS_TESTS) \
audio_area_unittest \
audio_format_unittest \
audio_thread_unittest \
- a2dp_info_unittest \
- a2dp_iodev_unittest \
alert_unittest \
alsa_card_unittest \
alsa_helpers_unittest \
- alsa_io_unittest \
alsa_jack_unittest \
alsa_mixer_unittest \
alsa_ucm_unittest \
array_unittest \
- bt_device_unittest \
- bt_io_unittest \
byte_buffer_unittest \
card_config_unittest \
checksum_unittest \
cras_client_unittest \
cras_tm_unittest \
+ device_monitor_unittest \
dev_stream_unittest \
device_blacklist_unittest \
dsp_core_unittest \
@@ -180,23 +292,26 @@ TESTS = \
dumper_unittest \
edid_utils_unittest \
expr_unittest \
+ file_wait_unittest \
fmt_conv_unittest \
hfp_info_unittest \
- hfp_iodev_unittest \
- hfp_slc_unittest \
buffer_share_unittest \
iodev_list_unittest \
iodev_unittest \
loopback_iodev_unittest \
mix_unittest \
linear_resampler_unittest \
+ observer_unittest \
+ ramp_unittest \
rate_estimator_unittest \
rclient_unittest \
rstream_unittest \
shm_unittest \
+ server_metrics_unittest \
softvol_curve_unittest \
stream_list_unittest \
system_state_unittest \
+ utf8_unittest \
util_unittest \
volume_curve_unittest
@@ -209,6 +324,21 @@ cras_test_client_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/libcras \
tests/cras_test_client.c: common/cras_version.h
+cras_monitor_SOURCES = tests/cras_monitor.c
+cras_monitor_LDADD = -lm libcras.la
+cras_monitor_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/libcras \
+ -I$(top_srcdir)/src/common -I$(top_builddir)/src/common
+
+tests/cras_monitor.c: common/cras_version.h
+
+cras_router_SOURCES = tests/cras_router.c
+cras_router_LDADD = -lm libcras.la
+cras_router_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/libcras \
+ -I$(top_srcdir)/src/common -I$(top_srcdir)/src/dsp \
+ -I$(top_srcdir)/src/server -I$(top_builddir)/src/common
+
+tests/cras_router.c: common/cras_version.h
+
CLEANFILES = common/cras_version.h
.PHONY: common/cras_version.h
common/cras_version.h:
@@ -228,6 +358,7 @@ check_PROGRAMS += \
crossover_test \
crossover2_test \
drc_test \
+ dsp_util_test \
eq_test \
eq2_test \
cmpraw
@@ -248,6 +379,10 @@ drc_test_SOURCES = dsp/drc.c dsp/drc_kernel.c dsp/drc_math.c \
drc_test_LDADD = -lrt -lm
drc_test_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/dsp
+dsp_util_test_SOURCES = dsp/tests/dsp_util_test.c dsp/dsp_util.c
+dsp_util_test_LDADD = -lm
+dsp_util_test_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/dsp -Wno-error=strict-aliasing
+
eq_test_SOURCES = dsp/biquad.c dsp/eq.c dsp/dsp_util.c dsp/tests/eq_test.c \
dsp/tests/dsp_test_util.c dsp/tests/raw.c
eq_test_LDADD = -lrt -lm
@@ -271,7 +406,8 @@ alert_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
alert_unittest_LDADD = -lgtest -lpthread
alsa_card_unittest_SOURCES = tests/alsa_card_unittest.cc \
- server/cras_alsa_card.c
+ server/cras_alsa_card.c server/cras_alsa_mixer_name.c \
+ server/cras_alsa_ucm_section.c
alsa_card_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
-I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/server \
@@ -296,6 +432,7 @@ audio_format_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/server \
-I$(top_srcdir)/src/common
audio_format_unittest_LDADD = -lgtest -lpthread
+if HAVE_DBUS
a2dp_info_unittest_SOURCES = tests/a2dp_info_unittest.cc \
server/cras_a2dp_info.c
a2dp_info_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/server \
@@ -307,27 +444,37 @@ a2dp_iodev_unittest_SOURCES = tests/a2dp_iodev_unittest.cc \
a2dp_iodev_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/server \
-I$(top_srcdir)/src/common $(DBUS_CFLAGS)
a2dp_iodev_unittest_LDADD = -lgtest -lpthread $(DBUS_LIBS)
+endif
alsa_io_unittest_SOURCES = tests/alsa_io_unittest.cc server/softvol_curve.c \
- common/sfh.c
+ common/sfh.c \
+ server/cras_alsa_ucm_section.c \
+ server/cras_alsa_mixer_name.c
alsa_io_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/server \
- -I$(top_srcdir)/src/common $(DBUS_CFLAGS)
+ -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/server/config
alsa_io_unittest_LDADD = -lgtest -lpthread
alsa_jack_unittest_SOURCES = tests/alsa_jack_unittest.cc \
- server/cras_alsa_jack.c
+ server/cras_alsa_jack.c \
+ server/cras_alsa_ucm_section.c \
+ server/cras_alsa_mixer_name.c
alsa_jack_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/server
alsa_jack_unittest_LDADD = -lgtest -lpthread
-alsa_mixer_unittest_SOURCES = tests/alsa_mixer_unittest.cc
+alsa_mixer_unittest_SOURCES = tests/alsa_mixer_unittest.cc \
+ server/cras_alsa_mixer_name.c \
+ server/cras_alsa_ucm_section.c
alsa_mixer_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
-I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/server \
-I$(top_srcdir)/src/server/config
alsa_mixer_unittest_LDADD = -lgtest -lpthread
-alsa_ucm_unittest_SOURCES = tests/alsa_ucm_unittest.cc
+alsa_ucm_unittest_SOURCES = tests/alsa_ucm_unittest.cc \
+ server/cras_alsa_mixer_name.c \
+ server/cras_alsa_ucm_section.c
alsa_ucm_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
-I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/server \
@@ -343,32 +490,28 @@ audio_thread_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
-I$(top_srcdir)/src/common -I$(top_srcdir)/src/server
audio_thread_unittest_LDADD = -lgtest -lpthread -lrt
+if HAVE_DBUS
bt_device_unittest_SOURCES = tests/bt_device_unittest.cc \
server/cras_bt_device.c
bt_device_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/server \
-I$(top_srcdir)/src/common $(DBUS_CFLAGS)
bt_device_unittest_LDADD = -lgtest -lpthread $(DBUS_LIBS)
-bt_io_unittest_SOURCES = tests/bt_io_unittest.cc
+bt_io_unittest_SOURCES = tests/bt_io_unittest.cc common/sfh.c
bt_io_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/server \
-I$(top_srcdir)/src/common $(DBUS_CFLAGS)
bt_io_unittest_LDADD = -lgtest -lpthread $(DBUS_LIBS)
-
-bt_profile_unittest_SOURCES = tests/bt_profile_unittest.cc tests/dbus_test.cc \
- server/cras_bt_profile.c
-bt_profile_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
- -I$(top_srcdir)/src/server $(DBUS_CFLAGS)
-bt_profile_unittest_LDADD = -lgtest -lpthread $(DBUS_LIBS)
+endif
byte_buffer_unittest_SOURCES = tests/byte_buffer_unittest.cc
-byte_buffer_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/server
+byte_buffer_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common
byte_buffer_unittest_LDADD = -lgtest -lpthread
card_config_unittest_SOURCES = tests/card_config_unittest.cc \
server/config/cras_card_config.c
card_config_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
-I$(top_srcdir)/src/common -I$(top_srcdir)/src/server \
- -I$(top_srcdir)/src/server/config
+ -I$(top_srcdir)/src/server/config $(CRAS_UT_TMPDIR_CFLAGS)
card_config_unittest_LDADD = -lgtest -liniparser -lpthread
checksum_unittest_SOURCES = tests/checksum_unittest.cc common/cras_checksum.c
@@ -376,7 +519,7 @@ checksum_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common
checksum_unittest_LDADD = -lgtest -lpthread
cras_client_unittest_SOURCES = tests/cras_client_unittest.cc \
- common/cras_config.c common/cras_util.c
+ common/cras_config.c common/cras_util.c common/cras_file_wait.c
cras_client_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/libcras
cras_client_unittest_LDADD = -lgtest -lpthread -lspeexdsp
@@ -396,9 +539,14 @@ device_blacklist_unittest_SOURCES = tests/device_blacklist_unittest.cc \
server/config/cras_device_blacklist.c
device_blacklist_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
-I$(top_srcdir)/src/common -I$(top_srcdir)/src/server \
- -I$(top_srcdir)/src/server/config
+ -I$(top_srcdir)/src/server/config $(CRAS_UT_TMPDIR_CFLAGS)
device_blacklist_unittest_LDADD = -lgtest -liniparser -lpthread
+device_monitor_unittest_SOURCES = tests/device_monitor_unittest.cc
+device_monitor_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/server
+device_monitor_unittest_LDADD = -lgtest -lpthread
+
dsp_core_unittest_SOURCES = tests/dsp_core_unittest.cc dsp/eq.c dsp/eq2.c \
dsp/biquad.c dsp/dsp_util.c dsp/crossover.c dsp/crossover2.c dsp/drc.c \
dsp/drc_kernel.c dsp/drc_math.c
@@ -439,6 +587,12 @@ expr_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/server
expr_unittest_LDADD = -lgtest -lpthread
+file_wait_unittest_SOURCES = tests/file_wait_unittest.cc \
+ common/cras_file_wait.c common/cras_util.c
+file_wait_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
+ $(CRAS_UT_TMPDIR_CFLAGS)
+file_wait_unittest_LDADD = -lgtest -lpthread
+
fmt_conv_unittest_SOURCES = tests/fmt_conv_unittest.cc server/cras_fmt_conv.c
fmt_conv_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/server
@@ -449,6 +603,7 @@ hfp_info_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/server
hfp_info_unittest_LDADD = -lgtest -lpthread
+if HAVE_DBUS
hfp_iodev_unittest_SOURCES = tests/hfp_iodev_unittest.cc \
server/cras_hfp_iodev.c common/sfh.c
hfp_iodev_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
@@ -460,6 +615,7 @@ hfp_slc_unittest_SOURCES = tests/hfp_slc_unittest.cc \
hfp_slc_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/server $(DBUS_CFLAGS)
hfp_slc_unittest_LDADD = -lgtest -lpthread $(DBUS_LIBS)
+endif
buffer_share_unittest_SOURCES = tests/buffer_share_unittest.cc \
server/buffer_share.c
@@ -489,7 +645,13 @@ iodev_unittest_LDADD = -lgtest -lpthread
mix_unittest_SOURCES = tests/mix_unittest.cc server/cras_mix.c
mix_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/server
-mix_unittest_LDADD = -lgtest -lpthread
+mix_unittest_LDADD = libcrasmix.la \
+ $(CRAS_SSE4_2) \
+ $(CRAS_AVX) \
+ $(CRAS_AVX2) \
+ $(CRAS_FMA) \
+ -lgtest \
+ -lpthread
linear_resampler_unittest_SOURCES = tests/linear_resampler_unittest.cc \
server/linear_resampler.c server/cras_audio_area.c
@@ -497,20 +659,36 @@ linear_resampler_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/comm
-I$(top_srcdir)/src/server
linear_resampler_unittest_LDADD = -lgtest -lpthread
+observer_unittest_SOURCES = tests/observer_unittest.cc
+observer_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/server
+observer_unittest_LDADD = -lgtest -lpthread
+
+ramp_unittest_SOURCES = tests/ramp_unittest.cc
+ramp_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/server
+ramp_unittest_LDADD = -lgtest -lpthread
+
rate_estimator_unittest_SOURCES = tests/rate_estimator_unittest.cc server/rate_estimator.c
rate_estimator_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/server
rate_estimator_unittest_LDADD = -lgtest -lpthread
-rclient_unittest_SOURCES = tests/rclient_unittest.cc server/cras_rclient.c
+rclient_unittest_SOURCES = tests/rclient_unittest.cc
rclient_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
- -I$(top_srcdir)/src/server
+ -I$(top_srcdir)/src/server $(CRAS_UT_TMPDIR_CFLAGS)
rclient_unittest_LDADD = -lgtest -lpthread
-rstream_unittest_SOURCES = tests/rstream_unittest.cc server/cras_rstream.c
+rstream_unittest_SOURCES = tests/rstream_unittest.cc server/cras_rstream.c \
+ common/cras_shm.c
rstream_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/server
-rstream_unittest_LDADD = -lasound -lgtest -lpthread
+rstream_unittest_LDADD = -lasound -lgtest -lpthread -lrt
+
+server_metrics_unittest_SOURCES = tests/server_metrics_unittest.cc
+server_metrics_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/server
+server_metrics_unittest_LDADD = -lgtest -lpthread
shm_unittest_SOURCES = tests/shm_unittest.cc
shm_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common
@@ -528,12 +706,17 @@ stream_list_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
stream_list_unittest_LDADD = -lgtest -lpthread
system_state_unittest_SOURCES = tests/system_state_unittest.cc \
- server/cras_system_state.c
+ server/cras_system_state.c common/cras_shm.c
system_state_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \
-I$(top_srcdir)/src/common -I$(top_srcdir)/src/server \
-I$(top_srcdir)/src/server/config
system_state_unittest_LDADD = -lgtest -lrt -lpthread
+utf8_unittest_SOURCES = tests/utf8_unittest.cc server/cras_utf8.c
+utf8_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/server
+utf8_unittest_LDADD = -lgtest -lpthread
+
util_unittest_SOURCES = tests/util_unittest.cc common/cras_util.c \
common/cras_config.c
util_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common
diff --git a/cras/src/arm/shm.S b/cras/src/arm/shm.S
deleted file mode 100644
index dcea684e..00000000
--- a/cras/src/arm/shm.S
+++ /dev/null
@@ -1,55 +0,0 @@
-#include <asm/unistd.h> /* For system call numbers. */
-#define MAX_ERRNO 4095 /* For recognizing system call error returns. */
-
-#define __bionic_asm_custom_entry(f)
-#define __bionic_asm_custom_end(f)
-#define __bionic_asm_function_type @function
-
-#include <machine/asm.h>
-
-#define ENTRY(f) \
- .text; \
- .globl f; \
- .align __bionic_asm_align; \
- .type f, __bionic_asm_function_type; \
- f: \
- __bionic_asm_custom_entry(f); \
- .cfi_startproc \
-
-#define END(f) \
- .cfi_endproc; \
- .size f, .-f; \
- __bionic_asm_custom_end(f) \
-
-ENTRY(shmat)
- mov ip, r7
- ldr r7, =__NR_shmat
- swi #0
- mov r7, ip
- cmn r0, #(MAX_ERRNO + 1)
- bxls lr
- neg r0, r0
- b __set_errno_internal
-END(shmat)
-
-ENTRY(shmdt)
- mov ip, r7
- ldr r7, =__NR_shmdt
- swi #0
- mov r7, ip
- cmn r0, #(MAX_ERRNO + 1)
- bxls lr
- neg r0, r0
- b __set_errno_internal
-END(shmdt)
-
-ENTRY(shmget)
- mov ip, r7
- ldr r7, =__NR_shmget
- swi #0
- mov r7, ip
- cmn r0, #(MAX_ERRNO + 1)
- bxls lr
- neg r0, r0
- b __set_errno_internal
-END(shmget)
diff --git a/cras/src/server/byte_buffer.h b/cras/src/common/byte_buffer.h
index c949b71c..c949b71c 100644
--- a/cras/src/server/byte_buffer.h
+++ b/cras/src/common/byte_buffer.h
diff --git a/cras/src/common/cras_audio_format.h b/cras/src/common/cras_audio_format.h
index a77bc84a..516a18d4 100644
--- a/cras/src/common/cras_audio_format.h
+++ b/cras/src/common/cras_audio_format.h
@@ -6,13 +6,41 @@
#ifndef CRAS_AUDIO_FORMAT_H_
#define CRAS_AUDIO_FORMAT_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <stdint.h>
#include <string.h>
#ifdef __ANDROID__
+#include <hardware/audio.h>
#include <tinyalsa/asoundlib.h>
#define PCM_FORMAT_WIDTH(format) pcm_format_to_bits(format)
typedef enum pcm_format snd_pcm_format_t;
+
+/* libasound audio formats. */
+#define SND_PCM_FORMAT_UNKNOWN -1
+#define SND_PCM_FORMAT_U8 1
+#define SND_PCM_FORMAT_S16_LE 2
+#define SND_PCM_FORMAT_S24_LE 6
+#define SND_PCM_FORMAT_S32_LE 10
+
+static inline int audio_format_to_cras_format(audio_format_t audio_format)
+{
+ switch (audio_format) {
+ case AUDIO_FORMAT_PCM_16_BIT:
+ return SND_PCM_FORMAT_S16_LE;
+ case AUDIO_FORMAT_PCM_8_BIT:
+ return SND_PCM_FORMAT_U8;
+ case AUDIO_FORMAT_PCM_32_BIT:
+ return SND_PCM_FORMAT_S32_LE;
+ case AUDIO_FORMAT_PCM_8_24_BIT:
+ return SND_PCM_FORMAT_S24_LE;
+ default:
+ return SND_PCM_FORMAT_UNKNOWN;
+ }
+}
#else
#include <alsa/asoundlib.h>
#define PCM_FORMAT_WIDTH(format) snd_pcm_format_physical_width(format)
@@ -125,4 +153,8 @@ void cras_channel_conv_matrix_destroy(float **mtx, size_t out_ch);
float **cras_channel_conv_matrix_create(const struct cras_audio_format *in,
const struct cras_audio_format *out);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* CRAS_AUDIO_FORMAT_H_ */
diff --git a/cras/src/common/cras_config.c b/cras/src/common/cras_config.c
index 9a8e8122..29e9fa1c 100644
--- a/cras/src/common/cras_config.c
+++ b/cras/src/common/cras_config.c
@@ -13,5 +13,5 @@ const char *cras_config_get_system_socket_file_dir()
/* This directory is created by the upstart script, eventually it would
* be nice to make this more dynamic, but it isn't needed right now for
* Chrome OS. */
- return "/var/run/cras";
+ return CRAS_SOCKET_FILE_DIR;
}
diff --git a/cras/src/common/cras_config.h b/cras/src/common/cras_config.h
index 96a84c78..c1731c73 100644
--- a/cras/src/common/cras_config.h
+++ b/cras/src/common/cras_config.h
@@ -12,7 +12,9 @@
#define CRAS_CLIENT_RT_THREAD_PRIORITY 10
#define CRAS_CLIENT_NICENESS_LEVEL -10
#define CRAS_SOCKET_FILE ".cras_socket"
-#define CRAS_CONFIG_FILE_DIR "/etc/cras"
+
+/* CRAS_CONFIG_FILE_DIR is defined as $sysconfdir/cras by the configure
+ script. */
/* Gets the path to save UDS socket files. */
const char *cras_config_get_system_socket_file_dir();
diff --git a/cras/src/common/cras_file_wait.c b/cras/src/common/cras_file_wait.c
new file mode 100644
index 00000000..809bc68c
--- /dev/null
+++ b/cras/src/common/cras_file_wait.c
@@ -0,0 +1,315 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/inotify.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "cras_file_wait.h"
+
+#define CRAS_FILE_WAIT_EVENT_MIN_SIZE sizeof(struct inotify_event)
+#define CRAS_FILE_WAIT_EVENT_SIZE (CRAS_FILE_WAIT_EVENT_MIN_SIZE + NAME_MAX + 1)
+#define CRAS_FILE_WAIT_FLAG_MOCK_RACE (1 << 31)
+
+struct cras_file_wait {
+ cras_file_wait_callback_t callback;
+ void *callback_context;
+ const char *file_path;
+ size_t file_path_len;
+ char *watch_path;
+ char *watch_dir;
+ char *watch_file_name;
+ size_t watch_file_name_len;
+ int inotify_fd;
+ int watch_id;
+ char event_buf[CRAS_FILE_WAIT_EVENT_SIZE];
+ cras_file_wait_flag_t flags;
+};
+
+int cras_file_wait_get_fd(struct cras_file_wait *file_wait)
+{
+ if (!file_wait)
+ return -EINVAL;
+ if (file_wait->inotify_fd < 0)
+ return -EINVAL;
+ return file_wait->inotify_fd;
+}
+
+/* Defined for the unittest. */
+void cras_file_wait_mock_race_condition(struct cras_file_wait *file_wait);
+void cras_file_wait_mock_race_condition(struct cras_file_wait *file_wait)
+{
+ if (file_wait)
+ file_wait->flags |= CRAS_FILE_WAIT_FLAG_MOCK_RACE;
+}
+
+void cras_file_wait_destroy(struct cras_file_wait *file_wait)
+{
+ if (!file_wait)
+ return;
+ if (file_wait->inotify_fd >= 0)
+ close(file_wait->inotify_fd);
+ free(file_wait);
+}
+
+static int cras_file_wait_rm_watch(struct cras_file_wait *file_wait)
+{
+ int rc;
+
+ file_wait->watch_path[0] = 0;
+ file_wait->watch_dir[0] = 0;
+ file_wait->watch_file_name[0] = 0;
+ file_wait->watch_file_name_len = 0;
+ if (file_wait->inotify_fd >= 0 && file_wait->watch_id >= 0) {
+ rc = inotify_rm_watch(file_wait->inotify_fd,
+ file_wait->watch_id);
+ file_wait->watch_id = -1;
+ if (rc < 0)
+ return -errno;
+ }
+ return 0;
+}
+
+int cras_file_wait_process_event(struct cras_file_wait *file_wait,
+ struct inotify_event *event)
+{
+ cras_file_wait_event_t file_wait_event;
+
+ syslog(LOG_DEBUG, "file_wait->watch_id: %d, event->wd: %d"
+ ", event->mask: %x, event->name: %s",
+ file_wait->watch_id, event->wd, event->mask,
+ event->len ? event->name : "");
+
+ if (event->wd != file_wait->watch_id)
+ return 0;
+
+ if (event->mask & IN_IGNORED) {
+ /* The watch has been removed. */
+ file_wait->watch_id = -1;
+ return cras_file_wait_rm_watch(file_wait);
+ }
+
+ if (event->len == 0 ||
+ memcmp(event->name, file_wait->watch_file_name,
+ file_wait->watch_file_name_len + 1) != 0) {
+ /* Some file we don't care about. */
+ return 0;
+ }
+
+ if ((event->mask & (IN_CREATE|IN_MOVED_TO)) != 0)
+ file_wait_event = CRAS_FILE_WAIT_EVENT_CREATED;
+ else if ((event->mask & (IN_DELETE|IN_MOVED_FROM)) != 0)
+ file_wait_event = CRAS_FILE_WAIT_EVENT_DELETED;
+ else
+ return 0;
+
+ /* Found the file! */
+ if (strcmp(file_wait->watch_path, file_wait->file_path) == 0) {
+ /* Tell the caller about this creation or deletion. */
+ file_wait->callback(file_wait->callback_context,
+ file_wait_event, event->name);
+ } else {
+ /* Remove the watch for this file, move on. */
+ return cras_file_wait_rm_watch(file_wait);
+ }
+ return 0;
+}
+
+int cras_file_wait_dispatch(struct cras_file_wait *file_wait)
+{
+ struct inotify_event *event;
+ char *watch_dir_end;
+ size_t watch_dir_len;
+ char *watch_file_start;
+ size_t watch_path_len;
+ int rc = 0;
+ int flags;
+ ssize_t read_rc;
+ ssize_t read_offset;
+
+ if (!file_wait)
+ return -EINVAL;
+
+ /* If we have a file-descriptor, then read it and see what's up. */
+ if (file_wait->inotify_fd >= 0) {
+ read_offset = 0;
+ read_rc = read(file_wait->inotify_fd, file_wait->event_buf,
+ CRAS_FILE_WAIT_EVENT_SIZE);
+ if (read_rc < 0) {
+ rc = -errno;
+ if ((rc == -EAGAIN || rc == -EWOULDBLOCK)
+ && file_wait->watch_id < 0) {
+ /* Really nothing to read yet: we need to
+ * setup a watch. */
+ rc = 0;
+ }
+ } else if (read_rc < CRAS_FILE_WAIT_EVENT_MIN_SIZE) {
+ rc = -EIO;
+ } else if (file_wait->watch_id < 0) {
+ /* Processing messages related to old watches. */
+ rc = 0;
+ } else while (rc == 0 && read_offset < read_rc) {
+ event = (struct inotify_event *)
+ (file_wait->event_buf + read_offset);
+ read_offset += sizeof(*event) + event->len;
+ rc = cras_file_wait_process_event(file_wait, event);
+ }
+ }
+
+ /* Report errors from above here. */
+ if (rc != 0)
+ return rc;
+
+ if (file_wait->watch_id >= 0) {
+ /* Assume that the watch that we have is the right one. */
+ return 0;
+ }
+
+ /* Initialize inotify if we haven't already. */
+ if (file_wait->inotify_fd < 0) {
+ file_wait->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
+ if (file_wait->inotify_fd < 0)
+ return -errno;
+ }
+
+ /* Figure out what we need to watch next. */
+ rc = -ENOENT;
+ strcpy(file_wait->watch_dir, file_wait->file_path);
+ watch_dir_len = file_wait->file_path_len;
+
+ while (rc == -ENOENT) {
+ strcpy(file_wait->watch_path, file_wait->watch_dir);
+ watch_path_len = watch_dir_len;
+
+ /* Find the end of the parent directory. */
+ watch_dir_end = file_wait->watch_dir + watch_dir_len - 1;
+ while (watch_dir_end > file_wait->watch_dir &&
+ *watch_dir_end != '/')
+ watch_dir_end--;
+ watch_file_start = watch_dir_end + 1;
+ /* Treat consecutive '/' characters as one. */
+ while (watch_dir_end > file_wait->watch_dir &&
+ *(watch_dir_end - 1) == '/')
+ watch_dir_end--;
+ watch_dir_len = watch_dir_end - file_wait->watch_dir;
+
+ if (watch_dir_len == 0) {
+ /* We're looking for a file in the current directory. */
+ strcpy(file_wait->watch_file_name,
+ file_wait->watch_path);
+ file_wait->watch_file_name_len = watch_path_len;
+ strcpy(file_wait->watch_dir, ".");
+ watch_dir_len = 1;
+ } else {
+ /* Copy out the file name that we're looking for, and
+ * mark the end of the directory path. */
+ strcpy(file_wait->watch_file_name, watch_file_start);
+ file_wait->watch_file_name_len =
+ watch_path_len -
+ (watch_file_start - file_wait->watch_dir);
+ *watch_dir_end = 0;
+ }
+
+ if (file_wait->flags & CRAS_FILE_WAIT_FLAG_MOCK_RACE) {
+ /* For testing only. */
+ mknod(file_wait->watch_path, S_IFREG | 0600, 0);
+ file_wait->flags &= ~CRAS_FILE_WAIT_FLAG_MOCK_RACE;
+ }
+
+ flags = IN_CREATE|IN_MOVED_TO|IN_DELETE|IN_MOVED_FROM;
+ file_wait->watch_id =
+ inotify_add_watch(file_wait->inotify_fd,
+ file_wait->watch_dir, flags);
+ if (file_wait->watch_id < 0) {
+ rc = -errno;
+ continue;
+ }
+
+ /* Satisfy the race condition between existence of the
+ * file and creation of the watch. */
+ rc = access(file_wait->watch_path, F_OK);
+ if (rc < 0) {
+ rc = -errno;
+ if (rc == -ENOENT) {
+ /* As expected, the file still doesn't exist. */
+ rc = 0;
+ }
+ continue;
+ }
+
+ /* The file we're looking for exists. */
+ if (strcmp(file_wait->watch_path, file_wait->file_path) == 0) {
+ file_wait->callback(file_wait->callback_context,
+ CRAS_FILE_WAIT_EVENT_CREATED,
+ file_wait->watch_file_name);
+ return 0;
+ }
+
+ /* Start over again. */
+ rc = cras_file_wait_rm_watch(file_wait);
+ if (rc != 0)
+ return rc;
+ rc = -ENOENT;
+ strcpy(file_wait->watch_dir, file_wait->file_path);
+ watch_dir_len = file_wait->file_path_len;
+ }
+
+ /* Get out for permissions problems for example. */
+ return rc;
+}
+
+int cras_file_wait_create(const char *file_path,
+ cras_file_wait_flag_t flags,
+ cras_file_wait_callback_t callback,
+ void *callback_context,
+ struct cras_file_wait **file_wait_out)
+{
+ struct cras_file_wait *file_wait;
+ size_t file_path_len;
+ int rc;
+
+ if (!file_path || !*file_path || !callback || !file_wait_out)
+ return -EINVAL;
+ *file_wait_out = NULL;
+
+ /* Create a struct cras_file_wait to track waiting for this file. */
+ file_path_len = strlen(file_path);
+ file_wait = (struct cras_file_wait *)
+ calloc(1, sizeof(*file_wait) + ((file_path_len + 1) * 5));
+ if (!file_wait)
+ return -ENOMEM;
+ file_wait->callback = callback;
+ file_wait->callback_context = callback_context;
+ file_wait->inotify_fd = -1;
+ file_wait->watch_id = -1;
+ file_wait->file_path_len = file_path_len;
+ file_wait->flags = flags;
+
+ /* We've allocated memory such that the file_path, watch_path,
+ * watch_dir, and watch_file_name data are appended to the end of
+ * our cras_file_wait structure. */
+ file_wait->file_path = (const char *)file_wait + sizeof(*file_wait);
+ file_wait->watch_path = (char *)file_wait->file_path +
+ file_path_len + 1;
+ file_wait->watch_dir = file_wait->watch_path + file_path_len + 1;
+ file_wait->watch_file_name = file_wait->watch_dir + file_path_len + 1;
+ memcpy((void *)file_wait->file_path, file_path, file_path_len + 1);
+
+ /* Setup the first watch. If that fails unexpectedly, then we destroy
+ * the file wait structure immediately. */
+ rc = cras_file_wait_dispatch(file_wait);
+ if (rc != 0)
+ cras_file_wait_destroy(file_wait);
+ else
+ *file_wait_out = file_wait;
+ return rc;
+}
diff --git a/cras/src/common/cras_file_wait.h b/cras/src/common/cras_file_wait.h
new file mode 100644
index 00000000..28035823
--- /dev/null
+++ b/cras/src/common/cras_file_wait.h
@@ -0,0 +1,129 @@
+/* Copyright 2016 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_FILE_WAIT_H_
+#define CRAS_FILE_WAIT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Structure used to track the current progress of a file wait. */
+struct cras_file_wait;
+
+/* Flags type for file wait. */
+typedef unsigned int cras_file_wait_flag_t;
+
+/* No flags. */
+#define CRAS_FILE_WAIT_FLAG_NONE ((cras_file_wait_flag_t)0)
+
+/* File wait events. */
+typedef enum cras_file_wait_event {
+ CRAS_FILE_WAIT_EVENT_NONE,
+ CRAS_FILE_WAIT_EVENT_CREATED,
+ CRAS_FILE_WAIT_EVENT_DELETED,
+} cras_file_wait_event_t;
+
+/*
+ * File wait callback function.
+ *
+ * Called for cras_file_wait events. Do not call cras_file_wait_destroy()
+ * from this function.
+ *
+ * Args:
+ * context - Context pointer passed to cras_file_wait_start().
+ * event - Event that has occurred related to this file wait.
+ * filename - Filename associated with the event.
+ */
+typedef void (*cras_file_wait_callback_t)(void *context,
+ cras_file_wait_event_t event,
+ const char *filename);
+
+/*
+ * Wait for the existence of a file.
+ *
+ * Setup a watch with the aim of determining if the given file path exists. If
+ * any parent directory is missing, then the appropriate watch is created to
+ * watch for the parent (or it's parent). Watches are created or renewed while
+ * this file wait structure exists.
+ *
+ * The callback function will be called with event CRAS_FILE_WAIT_EVENT_CREATED
+ * when the file is created, moved into the directory, or if it already exists
+ * when this function is called.
+ *
+ * After the file is found future deletion and creation events for the file can
+ * be observed using the same file_wait structure and callback. When the file
+ * is deleted or moved out of it's parent, the callback is called with event
+ * CRAS_FILE_WAIT_EVENT_DELETED.
+ *
+ * Call cras_file_wait_destroy() to cancel the wait anytime and cleanup
+ * resources.
+ *
+ * Args:
+ * file_path - Path of the file or directory that must exist.
+ * flags - CRAS_FILE_WAIT_FLAG_* values bit-wise orred together. Set to
+ * CRAS_FILE_WAIT_FLAG_NONE for now.
+ * callback - Callback function to execute to notify of file existence.
+ * callback_context - Context pointer passed to the callback function.
+ * file_wait_out - Pointer to file wait structure that is initialized.
+ *
+ * Returns:
+ * - 0 for success, or negative on error.
+ * - On error cras_file_wait_destroy() need not be called.
+ */
+int cras_file_wait_create(const char *file_path,
+ cras_file_wait_flag_t flags,
+ cras_file_wait_callback_t callback,
+ void *callback_context,
+ struct cras_file_wait **file_wait_out);
+
+/* Returns the file-descriptor to poll for a file wait.
+ *
+ * Poll for POLLIN on this file decriptor. When there is data available, call
+ * cras_file_wait_continue() on the associated file_wait structure.
+ *
+ * Args:
+ * file_wait - The associated cras_file_wait structure initialized by
+ * cras_file_wait_start().
+ *
+ * Returns:
+ * Non-negative file descriptor number, or -EINVAL if the file_wait
+ * structure is NULL or otherwise invalid.
+ */
+int cras_file_wait_get_fd(struct cras_file_wait *file_wait);
+
+/* Dispatch a file wait event.
+ *
+ * Call this function when the file descriptor from cras_file_wait_fd() has
+ * data ready (POLLIN). This function will call the callback provided to
+ * cras_file_wait_start when there is a relevant event.
+ *
+ * Args:
+ * file_wait - The associated cras_file_wait structure initialized by
+ * cras_file_wait_start().
+ *
+ * Returns:
+ * - 0 for success, non-zero on error.
+ * - -EAGAIN or -EWOULDBLOCK when this function would have blocked.
+ */
+int cras_file_wait_dispatch(struct cras_file_wait *file_wait);
+
+/* Destroy a file wait structure.
+ *
+ * This function can be called to cleanup a cras_file_wait structure, and it
+ * will interrupt any wait that is in progress; the pointer is subsequently
+ * invalid.
+ *
+ * Args:
+ * file_wait - The cras_file_wait structure initialized by
+ * cras_file_wait_start();
+ */
+void cras_file_wait_destroy(struct cras_file_wait *file_wait);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* CRAS_FILE_WAIT_H_ */
diff --git a/cras/src/common/cras_iodev_info.h b/cras/src/common/cras_iodev_info.h
index 857a361e..9b1dbb5f 100644
--- a/cras/src/common/cras_iodev_info.h
+++ b/cras/src/common/cras_iodev_info.h
@@ -13,16 +13,20 @@
#define CRAS_NODE_TYPE_BUFFER_SIZE 32
#define CRAS_NODE_MIC_POS_BUFFER_SIZE 128
#define CRAS_NODE_NAME_BUFFER_SIZE 64
+#define CRAS_NODE_HOTWORD_MODEL_BUFFER_SIZE 16
/* Identifying information about an IO device.
* idx - iodev index.
* name - Name displayed to the user.
* stable_id - ID that does not change due to device plug/unplug or reboot.
+ * stable_id_new - New stable_id, it will be deprecated and be put on
+ * stable_id.
*/
struct __attribute__ ((__packed__)) cras_iodev_info {
uint32_t idx;
char name[CRAS_IODEV_NAME_BUFFER_SIZE];
uint32_t stable_id;
+ uint32_t stable_id_new;
};
/* Identifying information about an ionode on an iodev.
@@ -34,9 +38,13 @@ struct __attribute__ ((__packed__)) cras_iodev_info {
* volume - per-node volume (0-100)
* capture_gain - per-node capture gain/attenuation (in 100*dBFS)
* 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.
+ * stable_id_new - New stable_id, it will be deprecated and be put on
+ * stable_id.
* 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.
*/
struct __attribute__ ((__packed__)) cras_ionode_info {
uint32_t iodev_idx;
@@ -48,9 +56,12 @@ struct __attribute__ ((__packed__)) cras_ionode_info {
int32_t capture_gain;
int32_t left_right_swapped;
uint32_t type_enum;
+ uint32_t stable_id;
+ uint32_t stable_id_new;
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];
};
/* This is used in the cras_client_set_node_attr API.
diff --git a/cras/src/common/cras_messages.h b/cras/src/common/cras_messages.h
index e678c19f..ab962dfa 100644
--- a/cras/src/common/cras_messages.h
+++ b/cras/src/common/cras_messages.h
@@ -19,6 +19,7 @@
#define CRAS_PROTO_VER 1
#define CRAS_SERV_MAX_MSG_SIZE 256
#define CRAS_CLIENT_MAX_MSG_SIZE 256
+#define CRAS_HOTWORD_NAME_MAX_SIZE 8
/* Message IDs. */
enum CRAS_SERVER_MESSAGE_ID {
@@ -44,6 +45,10 @@ enum CRAS_SERVER_MESSAGE_ID {
CRAS_SERVER_TEST_DEV_COMMAND,
CRAS_SERVER_SUSPEND,
CRAS_SERVER_RESUME,
+ CRAS_CONFIG_GLOBAL_REMIX,
+ CRAS_SERVER_GET_HOTWORD_MODELS,
+ CRAS_SERVER_SET_HOTWORD_MODEL,
+ CRAS_SERVER_REGISTER_NOTIFICATION,
};
enum CRAS_CLIENT_MESSAGE_ID {
@@ -51,6 +56,18 @@ enum CRAS_CLIENT_MESSAGE_ID {
CRAS_CLIENT_CONNECTED,
CRAS_CLIENT_STREAM_CONNECTED,
CRAS_CLIENT_AUDIO_DEBUG_INFO_READY,
+ CRAS_CLIENT_GET_HOTWORD_MODELS_READY,
+ /* System status messages */
+ CRAS_CLIENT_OUTPUT_VOLUME_CHANGED,
+ CRAS_CLIENT_OUTPUT_MUTE_CHANGED,
+ CRAS_CLIENT_CAPTURE_GAIN_CHANGED,
+ CRAS_CLIENT_CAPTURE_MUTE_CHANGED,
+ CRAS_CLIENT_NODES_CHANGED,
+ CRAS_CLIENT_ACTIVE_NODE_CHANGED,
+ CRAS_CLIENT_OUTPUT_NODE_VOLUME_CHANGED,
+ CRAS_CLIENT_NODE_LEFT_RIGHT_SWAPPED_CHANGED,
+ CRAS_CLIENT_INPUT_NODE_GAIN_CHANGED,
+ CRAS_CLIENT_NUM_ACTIVE_STREAMS_CHANGED,
};
/* Messages that control the server. These are sent from the client to affect
@@ -352,6 +369,74 @@ static inline void cras_fill_suspend_message(struct cras_server_message *m,
m->length = sizeof(*m);
}
+/* Configures the global remix converter. */
+struct __attribute__ ((__packed__)) cras_config_global_remix {
+ struct cras_server_message header;
+ unsigned int num_channels;
+ float coefficient[];
+};
+
+static inline void cras_fill_config_global_remix_command(
+ struct cras_config_global_remix *m,
+ unsigned int num_channels,
+ float *coeff,
+ unsigned int count)
+{
+ m->header.id = CRAS_CONFIG_GLOBAL_REMIX;
+ m->header.length = sizeof(*m) + count * sizeof(*coeff);
+ m->num_channels = num_channels;
+ memcpy(m->coefficient, coeff, count * sizeof(*coeff));
+}
+
+/* Get supported hotword models. */
+struct __attribute__ ((__packed__)) cras_get_hotword_models {
+ struct cras_server_message header;
+ cras_node_id_t node_id;
+};
+
+static inline void cras_fill_get_hotword_models_message(
+ struct cras_get_hotword_models *m,
+ cras_node_id_t node_id)
+{
+ m->header.id = CRAS_SERVER_GET_HOTWORD_MODELS;
+ m->header.length = sizeof(*m);
+ m->node_id = node_id;
+}
+
+/* Set desired hotword model. */
+struct __attribute__ ((__packed__)) cras_set_hotword_model {
+ struct cras_server_message header;
+ cras_node_id_t node_id;
+ char model_name[CRAS_HOTWORD_NAME_MAX_SIZE];
+};
+
+static inline void cras_fill_set_hotword_model_message(
+ struct cras_set_hotword_model *m,
+ cras_node_id_t node_id,
+ const char *model_name)
+{
+ m->header.id = CRAS_SERVER_SET_HOTWORD_MODEL;
+ m->header.length = sizeof(*m);
+ m->node_id = node_id;
+ memcpy(m->model_name, model_name, CRAS_HOTWORD_NAME_MAX_SIZE);
+}
+
+struct __attribute__ ((__packed__)) cras_register_notification {
+ struct cras_server_message header;
+ uint32_t msg_id;
+ int do_register;
+};
+static inline void cras_fill_register_notification_message(
+ struct cras_register_notification *m,
+ enum CRAS_CLIENT_MESSAGE_ID msg_id,
+ int do_register)
+{
+ m->header.id = CRAS_SERVER_REGISTER_NOTIFICATION;
+ m->header.length = sizeof(*m);
+ m->msg_id = msg_id;
+ m->do_register = do_register;
+}
+
/*
* Messages sent from server to client.
*/
@@ -360,27 +445,25 @@ static inline void cras_fill_suspend_message(struct cras_server_message *m,
struct __attribute__ ((__packed__)) cras_client_connected {
struct cras_client_message header;
uint32_t client_id;
- key_t shm_key;
};
static inline void cras_fill_client_connected(
struct cras_client_connected *m,
- size_t client_id,
- key_t shm_key)
+ size_t client_id)
{
m->client_id = client_id;
- m->shm_key = shm_key;
m->header.id = CRAS_CLIENT_CONNECTED;
m->header.length = sizeof(struct cras_client_connected);
}
-/* Reply from server that a stream has been successfully added. */
+/*
+ * Reply from server that a stream has been successfully added.
+ * Two file descriptors are added, input shm followed by out shm.
+ */
struct __attribute__ ((__packed__)) cras_client_stream_connected {
struct cras_client_message header;
int32_t err;
cras_stream_id_t stream_id;
struct cras_audio_format_packed format;
- int32_t input_shm_key;
- int32_t output_shm_key;
uint32_t shm_max_size;
};
static inline void cras_fill_client_stream_connected(
@@ -388,15 +471,11 @@ static inline void cras_fill_client_stream_connected(
int err,
cras_stream_id_t stream_id,
struct cras_audio_format *format,
- int input_shm_key,
- int output_shm_key,
size_t shm_max_size)
{
m->err = err;
m->stream_id = stream_id;
pack_cras_audio_format(&m->format, format);
- m->input_shm_key = input_shm_key;
- m->output_shm_key = output_shm_key;
m->shm_max_size = shm_max_size;
m->header.id = CRAS_CLIENT_STREAM_CONNECTED;
m->header.length = sizeof(struct cras_client_stream_connected);
@@ -413,6 +492,148 @@ static inline void cras_fill_client_audio_debug_info_ready(
m->header.length = sizeof(*m);
}
+/* Sent from server to client when hotword models info is ready. */
+struct cras_client_get_hotword_models_ready {
+ struct cras_client_message header;
+ int32_t hotword_models_size;
+ uint8_t hotword_models[0];
+};
+static inline void cras_fill_client_get_hotword_models_ready(
+ struct cras_client_get_hotword_models_ready *m,
+ const char *hotword_models,
+ size_t hotword_models_size)
+{
+ m->header.id = CRAS_CLIENT_GET_HOTWORD_MODELS_READY;
+ m->header.length = sizeof(*m) + hotword_models_size;
+ m->hotword_models_size = hotword_models_size;
+ memcpy(m->hotword_models, hotword_models, hotword_models_size);
+}
+
+/* System status messages sent from server to client when state changes. */
+struct __attribute__ ((__packed__)) cras_client_volume_changed {
+ struct cras_client_message header;
+ int32_t volume;
+};
+static inline void cras_fill_client_output_volume_changed(
+ struct cras_client_volume_changed *m, int32_t volume)
+{
+ m->header.id = CRAS_CLIENT_OUTPUT_VOLUME_CHANGED;
+ m->header.length = sizeof(*m);
+ m->volume = volume;
+}
+static inline void cras_fill_client_capture_gain_changed(
+ struct cras_client_volume_changed *m, int32_t gain)
+{
+ m->header.id = CRAS_CLIENT_CAPTURE_GAIN_CHANGED;
+ m->header.length = sizeof(*m);
+ m->volume = gain;
+}
+
+struct __attribute__ ((__packed__)) cras_client_mute_changed {
+ struct cras_client_message header;
+ int32_t muted;
+ int32_t user_muted;
+ int32_t mute_locked;
+};
+static inline void cras_fill_client_output_mute_changed(
+ struct cras_client_mute_changed *m, int32_t muted,
+ int32_t user_muted, int32_t mute_locked)
+{
+ m->header.id = CRAS_CLIENT_OUTPUT_MUTE_CHANGED;
+ m->header.length = sizeof(*m);
+ m->muted = muted;
+ m->user_muted = user_muted;
+ m->mute_locked = mute_locked;
+}
+static inline void cras_fill_client_capture_mute_changed(
+ struct cras_client_mute_changed *m, int32_t muted,
+ int32_t mute_locked)
+{
+ m->header.id = CRAS_CLIENT_CAPTURE_MUTE_CHANGED;
+ m->header.length = sizeof(*m);
+ m->muted = muted;
+ m->user_muted = 0;
+ m->mute_locked = mute_locked;
+}
+
+struct __attribute__ ((__packed__)) cras_client_nodes_changed {
+ struct cras_client_message header;
+};
+static inline void cras_fill_client_nodes_changed(
+ struct cras_client_nodes_changed *m)
+{
+ m->header.id = CRAS_CLIENT_NODES_CHANGED;
+ m->header.length = sizeof(*m);
+}
+
+struct __attribute__ ((__packed__)) cras_client_active_node_changed {
+ struct cras_client_message header;
+ uint32_t direction;
+ cras_node_id_t node_id;
+};
+static inline void cras_fill_client_active_node_changed (
+ struct cras_client_active_node_changed *m,
+ enum CRAS_STREAM_DIRECTION direction,
+ cras_node_id_t node_id)
+{
+ m->header.id = CRAS_CLIENT_ACTIVE_NODE_CHANGED;
+ m->header.length = sizeof(*m);
+ m->direction = direction;
+ m->node_id = node_id;
+};
+
+struct __attribute__ ((__packed__)) cras_client_node_value_changed {
+ struct cras_client_message header;
+ cras_node_id_t node_id;
+ int32_t value;
+};
+static inline void cras_fill_client_output_node_volume_changed (
+ struct cras_client_node_value_changed *m,
+ cras_node_id_t node_id,
+ int32_t volume)
+{
+ m->header.id = CRAS_CLIENT_OUTPUT_NODE_VOLUME_CHANGED;
+ m->header.length = sizeof(*m);
+ m->node_id = node_id;
+ m->value = volume;
+};
+static inline void cras_fill_client_node_left_right_swapped_changed (
+ struct cras_client_node_value_changed *m,
+ cras_node_id_t node_id,
+ int swapped)
+{
+ m->header.id = CRAS_CLIENT_NODE_LEFT_RIGHT_SWAPPED_CHANGED;
+ m->header.length = sizeof(*m);
+ m->node_id = node_id;
+ m->value = swapped;
+};
+static inline void cras_fill_client_input_node_gain_changed (
+ struct cras_client_node_value_changed *m,
+ cras_node_id_t node_id,
+ int32_t gain)
+{
+ m->header.id = CRAS_CLIENT_INPUT_NODE_GAIN_CHANGED;
+ m->header.length = sizeof(*m);
+ m->node_id = node_id;
+ m->value = gain;
+};
+
+struct __attribute__ ((__packed__)) cras_client_num_active_streams_changed {
+ struct cras_client_message header;
+ uint32_t direction;
+ uint32_t num_active_streams;
+};
+static inline void cras_fill_client_num_active_streams_changed (
+ struct cras_client_num_active_streams_changed *m,
+ enum CRAS_STREAM_DIRECTION direction,
+ uint32_t num_active_streams)
+{
+ m->header.id = CRAS_CLIENT_NUM_ACTIVE_STREAMS_CHANGED;
+ m->header.length = sizeof(*m);
+ m->direction = direction;
+ m->num_active_streams = num_active_streams;
+};
+
/*
* Messages specific to passing audio between client and server
*/
diff --git a/cras/src/common/cras_observer_ops.h b/cras/src/common/cras_observer_ops.h
new file mode 100644
index 00000000..7265d98a
--- /dev/null
+++ b/cras/src/common/cras_observer_ops.h
@@ -0,0 +1,54 @@
+/* Copyright 2016 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_OBSERVER_OPS_H
+#define CRAS_OBSERVER_OPS_H
+
+#include "cras_types.h"
+
+/* Observation of CRAS state.
+ * Unless otherwise specified, all notifications only contain the data value
+ * reflecting the current state: it is possible that multiple notifications
+ * are queued within CRAS before being sent to the client.
+ */
+struct cras_observer_ops {
+ /* System output volume changed. */
+ void (*output_volume_changed)(void *context, int32_t volume);
+ /* System output mute changed. */
+ void (*output_mute_changed)(void *context,
+ int muted, int user_muted, int mute_locked);
+ /* System input/capture gain changed. */
+ void (*capture_gain_changed)(void *context, int32_t gain);
+ /* System input/capture mute changed. */
+ void (*capture_mute_changed)(void *context, int muted, int mute_locked);
+ /* Device or node topology changed. */
+ void (*nodes_changed)(void *context);
+ /* Active node changed. A notification is sent for every change.
+ * When there is no active node, node_id is 0. */
+ void (*active_node_changed)(void *context,
+ enum CRAS_STREAM_DIRECTION dir,
+ cras_node_id_t node_id);
+ /* Output node volume changed. */
+ void (*output_node_volume_changed)(void *context,
+ cras_node_id_t node_id,
+ int32_t volume);
+ /* Node left/right swapped state change. */
+ void (*node_left_right_swapped_changed)(void *context,
+ cras_node_id_t node_id,
+ int swapped);
+ /* Input gain changed. */
+ void (*input_node_gain_changed)(void *context,
+ cras_node_id_t node_id,
+ int32_t gain);
+ /* Suspend state changed. */
+ void (*suspend_changed)(void *context,
+ int suspended);
+ /* Number of active streams changed. */
+ void (*num_active_streams_changed)(void *context,
+ enum CRAS_STREAM_DIRECTION dir,
+ uint32_t num_active_streams);
+};
+
+#endif /* CRAS_OBSERVER_OPS_H */
diff --git a/cras/src/common/cras_shm.c b/cras/src/common/cras_shm.c
new file mode 100644
index 00000000..6b4085a9
--- /dev/null
+++ b/cras/src/common/cras_shm.c
@@ -0,0 +1,98 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/mman.h>
+#ifdef __BIONIC__
+#include <cutils/ashmem.h>
+#else
+#include <sys/shm.h>
+#endif
+#include <errno.h>
+#include <syslog.h>
+#include <string.h>
+
+#include "cras_shm.h"
+
+#ifdef __BIONIC__
+
+int cras_shm_open_rw (const char *name, size_t size)
+{
+ int fd;
+
+ /* Eliminate the / in the shm_name. */
+ if (name[0] == '/')
+ name++;
+ fd = ashmem_create_region(name, size);
+ if (fd < 0) {
+ fd = -errno;
+ syslog(LOG_ERR, "failed to ashmem_create_region %s: %s\n",
+ name, strerror(-fd));
+ }
+ return fd;
+}
+
+int cras_shm_reopen_ro (const char *name, int fd)
+{
+ /* After mmaping the ashmem read/write, change it's protection
+ bits to disallow further write access. */
+ if (ashmem_set_prot_region(fd, PROT_READ) != 0) {
+ fd = -errno;
+ syslog(LOG_ERR,
+ "failed to ashmem_set_prot_region %s: %s\n",
+ name, strerror(-fd));
+ }
+ return fd;
+}
+
+void cras_shm_close_unlink (const char *name, int fd)
+{
+ close(fd);
+}
+
+#else
+
+int cras_shm_open_rw (const char *name, size_t size)
+{
+ int fd;
+ int rc;
+
+ fd = shm_open(name, O_CREAT | O_EXCL | O_RDWR, 0600);
+ if (fd < 0) {
+ fd = -errno;
+ syslog(LOG_ERR, "failed to shm_open %s: %s\n",
+ name, strerror(-fd));
+ return fd;
+ }
+ rc = ftruncate(fd, size);
+ if (rc) {
+ rc = -errno;
+ syslog(LOG_ERR, "failed to set size of shm %s: %s\n",
+ name, strerror(-rc));
+ return rc;
+ }
+ return fd;
+}
+
+int cras_shm_reopen_ro (const char *name, int fd)
+{
+ /* Open a read-only copy to dup and pass to clients. */
+ fd = shm_open(name, O_RDONLY, 0);
+ if (fd < 0) {
+ fd = -errno;
+ syslog(LOG_ERR,
+ "Failed to re-open shared memory '%s' read-only: %s",
+ name, strerror(-fd));
+ }
+ return fd;
+}
+
+void cras_shm_close_unlink (const char *name, int fd)
+{
+ shm_unlink(name);
+ close(fd);
+}
+
+#endif
diff --git a/cras/src/common/cras_shm.h b/cras/src/common/cras_shm.h
index 0033e69c..9a541328 100644
--- a/cras/src/common/cras_shm.h
+++ b/cras/src/common/cras_shm.h
@@ -106,14 +106,30 @@ unsigned cras_shm_check_write_offset(const struct cras_audio_shm *shm,
return offset;
}
-/* Get a pointer to the current read buffer */
+/* Get the number of frames readable in current read buffer */
static inline
-uint8_t *cras_shm_get_curr_read_buffer(const struct cras_audio_shm *shm)
+unsigned cras_shm_get_curr_read_frames(const struct cras_audio_shm *shm)
{
unsigned i = shm->area->read_buf_idx & CRAS_SHM_BUFFERS_MASK;
+ unsigned read_offset, write_offset;
- return cras_shm_buff_for_idx(shm, i) +
+ read_offset =
cras_shm_check_read_offset(shm, shm->area->read_offset[i]);
+ write_offset =
+ cras_shm_check_write_offset(shm, shm->area->write_offset[i]);
+
+ if (read_offset > write_offset)
+ return 0;
+ else
+ return (write_offset - read_offset) / shm->config.frame_bytes;
+}
+
+/* Get the base of the current read buffer. */
+static inline
+uint8_t *cras_shm_get_read_buffer_base(const struct cras_audio_shm *shm)
+{
+ unsigned i = shm->area->read_buf_idx & CRAS_SHM_BUFFERS_MASK;
+ return cras_shm_buff_for_idx(shm, i);
}
/* Get the base of the current write buffer. */
@@ -246,23 +262,27 @@ size_t cras_shm_get_num_writeable(const struct cras_audio_shm *shm)
return shm->config.used_size / shm->config.frame_bytes;
}
-/* Flags an overrun if writing would cause one. */
-static inline void cras_shm_check_write_overrun(struct cras_audio_shm *shm)
+/* Flags an overrun if writing would cause one and reset the write offset.
+ * Return 1 if overrun happens, otherwise return 0. */
+static inline int cras_shm_check_write_overrun(struct cras_audio_shm *shm)
{
+ int ret = 0;
size_t write_buf_idx = shm->area->write_buf_idx & CRAS_SHM_BUFFERS_MASK;
- size_t read_buf_idx = shm->area->read_buf_idx & CRAS_SHM_BUFFERS_MASK;
if (!shm->area->write_in_progress[write_buf_idx]) {
unsigned int used_size = shm->config.used_size;
- if (write_buf_idx != read_buf_idx)
+ if (shm->area->write_offset[write_buf_idx]) {
shm->area->num_overruns++; /* Will over-write unread */
+ ret = 1;
+ }
memset(cras_shm_buff_for_idx(shm, write_buf_idx), 0, used_size);
shm->area->write_in_progress[write_buf_idx] = 1;
shm->area->write_offset[write_buf_idx] = 0;
}
+ return ret;
}
/* Increment the write pointer for the current buffer. */
@@ -463,4 +483,31 @@ static inline void cras_shm_copy_shared_config(struct cras_audio_shm *shm)
memcpy(&shm->config, &shm->area->config, sizeof(shm->config));
}
+/* Open a read/write shared memory area with the given name.
+ * Args:
+ * name - Name of the shared-memory area.
+ * size - Size of the shared-memory area.
+ * Returns:
+ * >= 0 file descriptor value, or negative errno value on error.
+ */
+int cras_shm_open_rw (const char *name, size_t size);
+
+/* Reopen an existing shared memory area read-only.
+ * Args:
+ * name - Name of the shared-memory area.
+ * fd - Existing file descriptor.
+ * Returns:
+ * >= 0 new file descriptor value, or negative errno value on error.
+ */
+int cras_shm_reopen_ro (const char *name, int fd);
+
+/* Close and delete a shared memory area.
+ * Args:
+ * name - Name of the shared-memory area.
+ * fd - Existing file descriptor.
+ * Returns:
+ * >= 0 new file descriptor value, or negative errno value on error.
+ */
+void cras_shm_close_unlink (const char *name, int fd);
+
#endif /* CRAS_SHM_H_ */
diff --git a/cras/src/common/cras_types.h b/cras/src/common/cras_types.h
index f88abc6a..6c0ce0f0 100644
--- a/cras/src/common/cras_types.h
+++ b/cras/src/common/cras_types.h
@@ -53,7 +53,7 @@ enum CRAS_TEST_IODEV_CMD {
enum CRAS_STREAM_DIRECTION {
CRAS_STREAM_OUTPUT,
CRAS_STREAM_INPUT,
- CRAS_STREAM_UNIFIED, /* This has been removed, don't use it. */
+ CRAS_STREAM_UNDEFINED,
CRAS_STREAM_POST_MIX_PRE_DSP,
CRAS_NUM_DIRECTIONS
};
@@ -105,8 +105,29 @@ static inline int cras_stream_is_loopback(enum CRAS_STREAM_DIRECTION dir)
/* Types of audio streams. */
enum CRAS_STREAM_TYPE {
CRAS_STREAM_TYPE_DEFAULT,
+ CRAS_STREAM_TYPE_MULTIMEDIA,
+ CRAS_STREAM_TYPE_VOICE_COMMUNICATION,
+ CRAS_STREAM_TYPE_SPEECH_RECOGNITION,
+ CRAS_STREAM_TYPE_PRO_AUDIO,
+ CRAS_STREAM_NUM_TYPES,
};
+#define ENUM_STR(x) case x: return #x;
+
+static inline const char *cras_stream_type_str(
+ enum CRAS_STREAM_TYPE stream_type)
+{
+ switch(stream_type) {
+ ENUM_STR(CRAS_STREAM_TYPE_DEFAULT)
+ ENUM_STR(CRAS_STREAM_TYPE_MULTIMEDIA)
+ ENUM_STR(CRAS_STREAM_TYPE_VOICE_COMMUNICATION)
+ ENUM_STR(CRAS_STREAM_TYPE_SPEECH_RECOGNITION)
+ ENUM_STR(CRAS_STREAM_TYPE_PRO_AUDIO)
+ default:
+ return "INVALID_STREAM_TYPE";
+ }
+}
+
/* Information about a client attached to the server. */
struct __attribute__ ((__packed__)) cras_attached_client_info {
uint32_t id;
@@ -139,6 +160,7 @@ static inline uint32_t node_index_of(cras_node_id_t id)
#define CRAS_MAX_IODEVS 20
#define CRAS_MAX_IONODES 20
#define CRAS_MAX_ATTACHED_CLIENTS 20
+#define CRAS_HOTWORD_STRING_SIZE 256
#define MAX_DEBUG_DEVS 4
#define MAX_DEBUG_STREAMS 8
#define AUDIO_THREAD_EVENT_LOG_SIZE (1024*6)
@@ -148,8 +170,11 @@ enum AUDIO_THREAD_LOG_EVENTS {
AUDIO_THREAD_WAKE,
AUDIO_THREAD_SLEEP,
AUDIO_THREAD_READ_AUDIO,
+ AUDIO_THREAD_READ_AUDIO_TSTAMP,
AUDIO_THREAD_READ_AUDIO_DONE,
+ AUDIO_THREAD_READ_OVERRUN,
AUDIO_THREAD_FILL_AUDIO,
+ AUDIO_THREAD_FILL_AUDIO_TSTAMP,
AUDIO_THREAD_FILL_AUDIO_DONE,
AUDIO_THREAD_WRITE_STREAMS_WAIT,
AUDIO_THREAD_WRITE_STREAMS_WAIT_TO,
@@ -175,6 +200,11 @@ enum AUDIO_THREAD_LOG_EVENTS {
AUDIO_THREAD_IODEV_CB,
AUDIO_THREAD_PB_MSG,
AUDIO_THREAD_ODEV_NO_STREAMS,
+ AUDIO_THREAD_ODEV_START,
+ AUDIO_THREAD_ODEV_LEAVE_NO_STREAMS,
+ AUDIO_THREAD_ODEV_DEFAULT_NO_STREAMS,
+ AUDIO_THREAD_FILL_ODEV_ZEROS,
+ AUDIO_THREAD_SEVERE_UNDERRUN,
};
struct __attribute__ ((__packed__)) audio_thread_event {
@@ -202,12 +232,15 @@ struct __attribute__ ((__packed__)) audio_dev_debug_info {
uint32_t num_channels;
double est_rate_ratio;
uint8_t direction;
+ uint32_t num_underruns;
+ uint32_t num_severe_underruns;
};
struct __attribute__ ((__packed__)) audio_stream_debug_info {
uint64_t stream_id;
uint32_t dev_idx;
uint32_t direction;
+ uint32_t stream_type;
uint32_t buffer_frames;
uint32_t cb_threshold;
uint32_t flags;
@@ -215,6 +248,7 @@ struct __attribute__ ((__packed__)) audio_stream_debug_info {
uint32_t num_channels;
uint32_t longest_fetch_sec;
uint32_t longest_fetch_nsec;
+ uint32_t num_overruns;
int8_t channel_layout[CRAS_CH_MAX];
};
@@ -238,6 +272,12 @@ struct __attribute__ ((__packed__)) audio_debug_info {
* mute_locked - 0 = unlocked, 1 = locked.
* suspended - 1 = suspended, 0 = resumed.
* capture_gain - Capture gain in dBFS * 100.
+ * capture_gain_target - Target capture gain in dBFS * 100. The actual
+ * capture gain will be subjected to current
+ * supported range. When active device/node changes,
+ * supported range changes accordingly. System state
+ * should try to re-apply target gain subjected to new
+ * range.
* capture_mute - 0 = unmuted, 1 = muted.
* capture_mute_locked - 0 = unlocked, 1 = locked.
* min_capture_gain - Min allowed capture gain in dBFS * 100.
@@ -264,7 +304,7 @@ struct __attribute__ ((__packed__)) audio_debug_info {
* use it.
*/
#define CRAS_SERVER_STATE_VERSION 2
-struct __attribute__ ((__packed__)) cras_server_state {
+struct __attribute__ ((packed, aligned(4))) cras_server_state {
uint32_t state_version;
uint32_t volume;
int32_t min_volume_dBFS;
@@ -274,6 +314,7 @@ struct __attribute__ ((__packed__)) cras_server_state {
int32_t mute_locked;
int32_t suspended;
int32_t capture_gain;
+ int32_t capture_gain_target;
int32_t capture_mute;
int32_t capture_mute_locked;
int32_t min_capture_gain;
@@ -310,6 +351,7 @@ enum cras_notify_device_action { /* Must match gavd action definitions. */
* lowered priority.
* usb_vendor_id - vendor ID if the device is on the USB bus.
* usb_product_id - product ID if the device is on the USB bus.
+ * usb_serial_number - serial number if the device is on the USB bus.
* usb_desc_checksum - the checksum of the USB descriptors if the device
* is on the USB bus.
*/
@@ -317,11 +359,13 @@ enum CRAS_ALSA_CARD_TYPE {
ALSA_CARD_TYPE_INTERNAL,
ALSA_CARD_TYPE_USB,
};
+#define USB_SERIAL_NUMBER_BUFFER_SIZE 64
struct __attribute__ ((__packed__)) cras_alsa_card_info {
enum CRAS_ALSA_CARD_TYPE card_type;
uint32_t card_index;
uint32_t usb_vendor_id;
uint32_t usb_product_id;
+ char usb_serial_number[USB_SERIAL_NUMBER_BUFFER_SIZE];
uint32_t usb_desc_checksum;
};
@@ -342,17 +386,36 @@ enum CRAS_NODE_TYPE {
CRAS_NODE_TYPE_INTERNAL_SPEAKER,
CRAS_NODE_TYPE_HEADPHONE,
CRAS_NODE_TYPE_HDMI,
+ CRAS_NODE_TYPE_HAPTIC,
+ CRAS_NODE_TYPE_LINEOUT,
/* These value can be used for input nodes. */
- CRAS_NODE_TYPE_INTERNAL_MIC,
CRAS_NODE_TYPE_MIC,
- CRAS_NODE_TYPE_AOKR,
+ CRAS_NODE_TYPE_HOTWORD,
CRAS_NODE_TYPE_POST_MIX_PRE_DSP,
CRAS_NODE_TYPE_POST_DSP,
/* These value can be used for both output and input nodes. */
CRAS_NODE_TYPE_USB,
CRAS_NODE_TYPE_BLUETOOTH,
- CRAS_NODE_TYPE_KEYBOARD_MIC,
CRAS_NODE_TYPE_UNKNOWN,
};
+/* Position values to described where a node locates on the system.
+ * NODE_POSITION_EXTERNAL - The node works only when peripheral
+ * is plugged.
+ * NODE_POSITION_INTERNAL - The node lives on the system and doesn't
+ * have specific direction.
+ * NODE_POSITION_FRONT - The node locates on the side of system that
+ * faces user.
+ * NODE_POSITION_REAR - The node locates on the opposite side of
+ * the system that faces user.
+ * NODE_POSITION_KEYBOARD - The node locates under the keyboard.
+ */
+enum CRAS_NODE_POSITION {
+ NODE_POSITION_EXTERNAL,
+ NODE_POSITION_INTERNAL,
+ NODE_POSITION_FRONT,
+ NODE_POSITION_REAR,
+ NODE_POSITION_KEYBOARD,
+};
+
#endif /* CRAS_TYPES_H_ */
diff --git a/cras/src/common/cras_util.c b/cras/src/common/cras_util.c
index ab8d9b73..bcc513ed 100644
--- a/cras/src/common/cras_util.c
+++ b/cras/src/common/cras_util.c
@@ -3,18 +3,25 @@
* found in the LICENSE file.
*/
+#define _GNU_SOURCE /* For ppoll() */
+
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
+#include <poll.h>
#include <sched.h>
+#include <stdlib.h>
#include <string.h>
+#include <syslog.h>
+#include <sys/param.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/syscall.h>
-#include <syslog.h>
#include <sys/types.h>
#include <unistd.h>
+#include "cras_util.h"
+
int cras_set_rt_scheduling(int rt_lim)
{
struct rlimit rl;
@@ -26,8 +33,6 @@ int cras_set_rt_scheduling(int rt_lim)
(unsigned) rt_lim, errno);
return -EACCES;
}
-
- syslog(LOG_INFO, "set rlimit success\n");
return 0;
}
@@ -40,8 +45,10 @@ int cras_set_thread_priority(int priority)
sched_param.sched_priority = priority;
err = pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param);
- if (err < 0)
- syslog(LOG_WARNING, "Set sched params for thread\n");
+ if (err)
+ syslog(LOG_WARNING,
+ "Failed to set thread sched params to priority %d"
+ ", rc: %d\n", priority, err);
return err;
}
@@ -56,7 +63,9 @@ int cras_set_nice_level(int nice)
* has been granted permission to adjust nice values on the system.
*/
rc = setpriority(PRIO_PROCESS, syscall(__NR_gettid), nice);
- syslog(LOG_DEBUG, "Set nice to %d %s.", nice, rc ? "Fail" : "Success");
+ if (rc)
+ syslog(LOG_WARNING, "Failed to set nice to %d, rc: %d",
+ nice, rc);
return rc;
}
@@ -85,58 +94,167 @@ int cras_make_fd_blocking(int fd)
return fcntl(fd, F_SETFL, fl & ~O_NONBLOCK);
}
-int cras_send_with_fd(int sockfd, void *buf, size_t len, int fd)
+int cras_send_with_fds(int sockfd, const void *buf, size_t len, int *fd,
+ unsigned int num_fds)
{
struct msghdr msg = {0};
struct iovec iov;
struct cmsghdr *cmsg;
- char control[CMSG_SPACE(sizeof(int))];
+ char *control;
+ const unsigned int control_size = CMSG_SPACE(sizeof(*fd) * num_fds);
+ int rc;
+
+ control = calloc(control_size, 1);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
- iov.iov_base = buf;
+ iov.iov_base = (void *)buf;
iov.iov_len = len;
msg.msg_control = control;
- msg.msg_controllen = sizeof(control);
+ msg.msg_controllen = control_size;
+
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
- msg.msg_controllen = cmsg->cmsg_len;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(*fd) * num_fds);
+ memcpy(CMSG_DATA(cmsg), fd, sizeof(*fd) * num_fds);
- return sendmsg(sockfd, &msg, 0);
+ rc = sendmsg(sockfd, &msg, 0);
+ free(control);
+ return rc;
}
-int cras_recv_with_fd(int sockfd, void *buf, size_t len, int *fd)
+int cras_recv_with_fds(int sockfd, void *buf, size_t len, int *fd,
+ unsigned int *num_fds)
{
struct msghdr msg = {0};
struct iovec iov;
struct cmsghdr *cmsg;
- char control[CMSG_SPACE(sizeof(int))];
+ char *control;
+ const unsigned int control_size = CMSG_SPACE(sizeof(*fd) * *num_fds);
int rc;
+ int i;
+
+ control = calloc(control_size, 1);
+
+ for (i = 0; i < *num_fds; i++)
+ fd[i] = -1;
- *fd = -1;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
iov.iov_base = buf;
iov.iov_len = len;
msg.msg_control = control;
- msg.msg_controllen = sizeof(control);
+ msg.msg_controllen = control_size;
rc = recvmsg(sockfd, &msg, 0);
- if (rc < 0)
- return rc;
+ if (rc < 0) {
+ rc = -errno;
+ goto exit;
+ }
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET
&& cmsg->cmsg_type == SCM_RIGHTS) {
- memcpy(fd, CMSG_DATA(cmsg), sizeof(*fd));
+ size_t fd_size = cmsg->cmsg_len - sizeof(*cmsg);
+ *num_fds = MIN(*num_fds, fd_size / sizeof(*fd));
+ memcpy(fd, CMSG_DATA(cmsg), *num_fds * sizeof(*fd));
break;
}
}
+exit:
+ free(control);
+ return rc;
+}
+
+int cras_poll(struct pollfd *fds, nfds_t nfds, struct timespec *timeout,
+ const sigset_t *sigmask)
+{
+ struct timespec now;
+ struct timespec future;
+ struct pollfd *fd = fds;
+ nfds_t i;
+ int rc = 0;
+
+ if (timeout) {
+ /* Treat a negative timeout as valid (but timed-out) since
+ * this function could update timeout to have negative tv_sec
+ * or tv_nsec. */
+ if (timeout->tv_sec < 0 || timeout->tv_nsec < 0)
+ return -ETIMEDOUT;
+ rc = clock_gettime(CLOCK_MONOTONIC_RAW, &future);
+ if (rc < 0)
+ return -errno;
+ add_timespecs(&future, timeout);
+ }
+
+ for (i = 0; i < nfds; i++) {
+ fd->revents = 0;
+ fd++;
+ }
+
+ rc = ppoll(fds, nfds, timeout, sigmask);
+ if (rc == 0 && timeout) {
+ rc = -ETIMEDOUT;
+ }
+ else if (rc < 0) {
+ rc = -errno;
+ }
+
+ if (timeout) {
+ clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+ subtract_timespecs(&future, &now, timeout);
+ }
+
return rc;
}
+
+int wait_for_dev_input_access()
+{
+ /* Wait for /dev/input/event* files to become accessible by
+ * having group 'input'. Setting these files to have 'rw'
+ * access to group 'input' is done through a udev rule
+ * installed by adhd into /lib/udev/rules.d.
+ *
+ * Wait for up to 2 seconds for the /dev/input/event* files to be
+ * readable by gavd.
+ *
+ * TODO(thutt): This could also be done with a udev enumerate
+ * and then a udev monitor.
+ */
+ const unsigned max_iterations = 4;
+ unsigned i = 0;
+
+ while (i < max_iterations) {
+ int readable;
+ struct timeval timeout;
+ const char * const pathname = "/dev/input/event0";
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 500000; /* 1/2 second. */
+ readable = access(pathname, R_OK);
+
+ /* If the file could be opened, then the udev rule has been
+ * applied and gavd can read the event files. If there are no
+ * event files, then we don't need to wait.
+ *
+ * If access does not become available, then headphone &
+ * microphone jack autoswitching will not function properly.
+ */
+ if (readable == 0 || (readable == -1 && errno == ENOENT)) {
+ /* Access allowed, or file does not exist. */
+ break;
+ }
+ if (readable != -1 || errno != EACCES) {
+ syslog(LOG_ERR, "Bad access for input devs.");
+ return errno;
+ }
+ select(1, NULL, NULL, NULL, &timeout);
+ ++i;
+ }
+
+ return 0;
+}
diff --git a/cras/src/common/cras_util.h b/cras/src/common/cras_util.h
index 30d301c9..237cff6f 100644
--- a/cras/src/common/cras_util.h
+++ b/cras/src/common/cras_util.h
@@ -10,6 +10,7 @@
extern "C" {
#endif
+#include <poll.h>
#include <time.h>
#include "cras_types.h"
@@ -75,12 +76,14 @@ int cras_make_fd_nonblocking(int fd);
/* Makes a file descriptor blocking. */
int cras_make_fd_blocking(int fd);
-/* Send data in buf to the socket with an extra file descriptor. */
-int cras_send_with_fd(int sockfd, const void *buf, size_t len, int fd);
+/* Send data in buf to the socket attach the fds. */
+int cras_send_with_fds(int sockfd, const void *buf, size_t len, int *fd,
+ unsigned int num_fds);
-/* Receive data in buf from the socket. If we also receive a file
-descriptor, put it in *fd, otherwise set *fd to -1. */
-int cras_recv_with_fd(int sockfd, const void *buf, size_t len, int *fd);
+/* Receive data in buf from the socket. If file descriptors are received, put
+ * them in *fd, otherwise set *fd to -1. */
+int cras_recv_with_fds(int sockfd, void *buf, size_t len, int *fd,
+ unsigned int *num_fds);
/* This must be written a million times... */
static inline void subtract_timespecs(const struct timespec *end,
@@ -154,6 +157,19 @@ static inline unsigned int timespec_to_ms(const struct timespec *ts)
return ts->tv_sec * 1000 + (ts->tv_nsec + 999999) / 1000000;
}
+/* Convert milliseconds to timespec. */
+static inline void ms_to_timespec(time_t milliseconds, struct timespec *ts)
+{
+ ts->tv_sec = milliseconds / 1000;
+ ts->tv_nsec = (milliseconds % 1000) * 1000000;
+}
+
+/* Returns non-zero if the given timespec is non-zero. */
+static inline int timespec_is_nonzero(const struct timespec *ts) {
+ return ts && (ts->tv_sec != 0 ||
+ (ts->tv_sec == 0 && ts->tv_nsec != 0));
+}
+
/* Calculates frames since time beg. */
static inline unsigned int cras_frames_since_time(const struct timespec *beg,
unsigned int rate)
@@ -168,6 +184,39 @@ static inline unsigned int cras_frames_since_time(const struct timespec *beg,
return cras_time_to_frames(&time_since, rate);
}
+/* Poll on the given file descriptors.
+ *
+ * See ppoll(). This implementation changes the value of timeout to the
+ * remaining time, and returns negative error codes on error.
+ *
+ * Args:
+ * fds - Array of pollfd structures.
+ * nfds - Number of pollfd structures.
+ * timeout - Timeout time updated upon return with remaining time. The
+ * timeout value may be updated to become invalid (negative
+ * tv_nsec or negative tv_sec). In that case, -tv_nsec is the
+ * number of nanoseconds by which the polling exceeded the
+ * supplied timeout. The function immediately returns with
+ * -ETIMEOUT if tv_nsec is negative, simplifying loops that
+ * rely on the returned remaining timeout.
+ * sigmask - Signal mask while in the poll.
+ *
+ * Returns:
+ * Positive when file decriptors are ready.
+ * Zero if no file descriptors are ready and timeout is NULL.
+ * -ETIMEDOUT when no file descriptors are ready and a timeout specified.
+ * Other negative error codes specified in the ppoll() man page.
+ */
+int cras_poll(struct pollfd *fds, nfds_t nfds, struct timespec *timeout,
+ const sigset_t *sigmask);
+
+/* Wait for /dev/input/event* files to become accessible.
+ *
+ * Returns:
+ * Zero on success. Otherwise a negative error code.
+ */
+int wait_for_dev_input_access();
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/cras/src/common/edid_utils.c b/cras/src/common/edid_utils.c
index 7664d19d..4a1a0a69 100644
--- a/cras/src/common/edid_utils.c
+++ b/cras/src/common/edid_utils.c
@@ -328,8 +328,8 @@ static inline void show_audio_dbc(FILE *outfile,
(edid_ext[dbp + DBCA_FORMAT]>>3) & 0xf;
unsigned char dbca_rate = edid_ext[dbp + DBCA_RATE];
- fprintf(outfile, "Audio: %d channel %s: ",
- edid_ext[dbp + DBCA_FORMAT] & 0x7,
+ fprintf(outfile, "Audio: %d channels %s: ",
+ (edid_ext[dbp + DBCA_FORMAT] & 0x7) + 1,
sad_audio_type[atype]);
if (dbca_rate & 0x40)
diff --git a/cras/src/dsp/drc.c b/cras/src/dsp/drc.c
index 5c63fd78..9f35debd 100644
--- a/cras/src/dsp/drc.c
+++ b/cras/src/dsp/drc.c
@@ -170,8 +170,8 @@ static void emphasis_stage_pair_biquads(float gain, float f1, float f2,
/* Initializes the emphasis and deemphasis filter */
static void init_emphasis_eq(struct drc *drc)
{
- struct biquad e = {0};
- struct biquad d = {0};
+ struct biquad e = { 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f };
+ struct biquad d = { 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f };
int i, j;
float stage_gain = drc_get_param(drc, 0, PARAM_FILTER_STAGE_GAIN);
@@ -256,40 +256,66 @@ static void free_kernel(struct drc *drc)
dk_free(&drc->kernel[i]);
}
-#if defined(__ARM_NEON__)
-#include <arm_neon.h>
-static void sum3(float *data, float *data1, float *data2, int n)
+// Note gcc 4.9+ with -O2 on aarch64 produces vectorized version of C
+// that is comparable performance, but twice as large. -O1 and -Os produce
+// small but slower code (4x slower than Neon).
+#if defined(__aarch64__)
+static void sum3(float *data, const float *data1, const float *data2, int n)
{
- float32x4_t x, y, z;
int count = n / 4;
int i;
if (count) {
__asm__ __volatile(
"1: \n"
- "vld1.32 {%e[x],%f[x]}, [%[data1]]! \n"
- "vld1.32 {%e[y],%f[y]}, [%[data2]]! \n"
- "vld1.32 {%e[z],%f[z]}, [%[data]] \n"
- "vadd.f32 %q[y], %q[x] \n"
- "vadd.f32 %q[z], %q[y] \n"
- "vst1.32 {%e[z],%f[z]}, [%[data]]! \n"
+ "ld1 {v0.4s}, [%[data1]], #16 \n"
+ "ld1 {v1.4s}, [%[data2]], #16 \n"
+ "ld1 {v2.4s}, [%[data]] \n"
+ "fadd v0.4s, v0.4s, v1.4s \n"
+ "fadd v0.4s, v0.4s, v2.4s \n"
+ "st1 {v0.4s}, [%[data]], #16 \n"
+ "subs %w[count], %w[count], #1 \n"
+ "b.ne 1b \n"
+ : /* output */
+ [data]"+r"(data),
+ [data1]"+r"(data1),
+ [data2]"+r"(data2),
+ [count]"+r"(count)
+ : /* input */
+ : /* clobber */
+ "v0", "v1", "v2", "memory", "cc"
+ );
+ }
+
+ n &= 3;
+ for (i = 0; i < n; i++)
+ data[i] += data1[i] + data2[i];
+}
+#elif defined(__ARM_NEON__)
+static void sum3(float *data, const float *data1, const float *data2, int n)
+{
+ int count = n / 4;
+ int i;
+
+ if (count) {
+ __asm__ __volatile(
+ "1: \n"
+ "vld1.32 {q0}, [%[data1]]! \n"
+ "vld1.32 {q1}, [%[data2]]! \n"
+ "vld1.32 {q2}, [%[data]] \n"
+ "vadd.f32 q0, q0, q1 \n"
+ "vadd.f32 q0, q0, q2 \n"
+ "vst1.32 {q0}, [%[data]]! \n"
"subs %[count], #1 \n"
"bne 1b \n"
: /* output */
- "=r"(data),
- "=r"(data1),
- "=r"(data2),
- "=r"(count),
- [x]"=&w"(x),
- [y]"=&w"(y),
- [z]"=&w"(z)
+ [data]"+r"(data),
+ [data1]"+r"(data1),
+ [data2]"+r"(data2),
+ [count]"+r"(count)
: /* input */
- [data]"0"(data),
- [data1]"1"(data1),
- [data2]"2"(data2),
- [count]"3"(count)
: /* clobber */
- "memory", "cc"
+ "q0", "q1", "q2", "memory", "cc"
);
}
@@ -299,7 +325,7 @@ static void sum3(float *data, float *data1, float *data2, int n)
}
#elif defined(__SSE3__)
#include <emmintrin.h>
-static void sum3(float *data, float *data1, float *data2, int n)
+static void sum3(float *data, const float *data1, const float *data2, int n)
{
__m128 x, y, z;
int count = n / 4;
@@ -320,18 +346,14 @@ static void sum3(float *data, float *data1, float *data2, int n)
"sub $1, %[count] \n"
"jne 1b \n"
: /* output */
- "=r"(data),
- "=r"(data1),
- "=r"(data2),
- "=r"(count),
- [x]"=&x"(x),
- [y]"=&x"(y),
- [z]"=&x"(z)
+ [data]"+r"(data),
+ [data1]"+r"(data1),
+ [data2]"+r"(data2),
+ [count]"+r"(count),
+ [x]"=x"(x),
+ [y]"=x"(y),
+ [z]"=x"(z)
: /* input */
- [data]"0"(data),
- [data1]"1"(data1),
- [data2]"2"(data2),
- [count]"3"(count)
: /* clobber */
"memory", "cc"
);
@@ -342,7 +364,7 @@ static void sum3(float *data, float *data1, float *data2, int n)
data[i] += data1[i] + data2[i];
}
#else
-static void sum3(float *data, float *data1, float *data2, int n)
+static void sum3(float *data, const float *data1, const float *data2, int n)
{
int i;
for (i = 0; i < n; i++)
diff --git a/cras/src/dsp/drc_kernel.c b/cras/src/dsp/drc_kernel.c
index 56154ada..bb922b60 100644
--- a/cras/src/dsp/drc_kernel.c
+++ b/cras/src/dsp/drc_kernel.c
@@ -406,42 +406,62 @@ static void dk_update_envelope(struct drc_kernel *dk)
/* For a division of frames, take the absolute values of left channel and right
* channel, store the maximum of them in output. */
-#ifdef __ARM_NEON__
-#include <arm_neon.h>
-static inline void max_abs_division(float *output, float *data0, float *data1)
+#if defined(__aarch64__)
+static inline void max_abs_division(float *output,
+ const float *data0, const float *data1)
+{
+ int count = DIVISION_FRAMES / 4;
+
+ __asm__ __volatile__(
+ "1: \n"
+ "ld1 {v0.4s}, [%[data0]], #16 \n"
+ "ld1 {v1.4s}, [%[data1]], #16 \n"
+ "fabs v0.4s, v0.4s \n"
+ "fabs v1.4s, v1.4s \n"
+ "fmax v0.4s, v0.4s, v1.4s \n"
+ "st1 {v0.4s}, [%[output]], #16 \n"
+ "subs %w[count], %w[count], #1 \n"
+ "b.ne 1b \n"
+ : /* output */
+ [data0]"+r"(data0),
+ [data1]"+r"(data1),
+ [output]"+r"(output),
+ [count]"+r"(count)
+ : /* input */
+ : /* clobber */
+ "v0", "v1", "memory", "cc"
+ );
+}
+#elif defined(__ARM_NEON__)
+static inline void max_abs_division(float *output,
+ const float *data0, const float *data1)
{
- float32x4_t x, y;
int count = DIVISION_FRAMES / 4;
__asm__ __volatile__(
"1: \n"
- "vld1.32 {%e[x],%f[x]}, [%[data0]]! \n"
- "vld1.32 {%e[y],%f[y]}, [%[data1]]! \n"
- "vabs.f32 %q[x], %q[x] \n"
- "vabs.f32 %q[y], %q[y] \n"
- "vmax.f32 %q[x], %q[y] \n"
- "vst1.32 {%e[x],%f[x]}, [%[output]]! \n"
+ "vld1.32 {q0}, [%[data0]]! \n"
+ "vld1.32 {q1}, [%[data1]]! \n"
+ "vabs.f32 q0, q0 \n"
+ "vabs.f32 q1, q1 \n"
+ "vmax.f32 q0, q1 \n"
+ "vst1.32 {q0}, [%[output]]! \n"
"subs %[count], #1 \n"
"bne 1b \n"
: /* output */
- "=r"(data0),
- "=r"(data1),
- "=r"(output),
- "=r"(count),
- [x]"=&w"(x),
- [y]"=&w"(y)
+ [data0]"+r"(data0),
+ [data1]"+r"(data1),
+ [output]"+r"(output),
+ [count]"+r"(count)
: /* input */
- [data0]"0"(data0),
- [data1]"1"(data1),
- [output]"2"(output),
- [count]"3"(count)
: /* clobber */
- "memory", "cc"
+ "q0", "q1", "memory", "cc"
);
}
#elif defined(__SSE3__)
#include <emmintrin.h>
-static inline void max_abs_division(float *output, float *data0, float *data1)
+static inline void max_abs_division(float *output,
+ const float *data0, const float *data1)
{
__m128 x, y;
int count = DIVISION_FRAMES / 4;
@@ -473,7 +493,8 @@ static inline void max_abs_division(float *output, float *data0, float *data1)
);
}
#else
-static inline void max_abs_division(float *output, float *data0, float *data1)
+static inline void max_abs_division(float *output,
+ const float *data0, const float *data1)
{
int i;
for (i = 0; i < DIVISION_FRAMES; i++)
@@ -545,6 +566,7 @@ static void dk_update_detector_average(struct drc_kernel *dk)
/* Calculate compress_gain from the envelope and apply total_gain to compress
* the next output division. */
+/* TODO(fbarchard): Port to aarch64 */
#if defined(__ARM_NEON__)
#include <arm_neon.h>
static void dk_compress_output(struct drc_kernel *dk)
diff --git a/cras/src/dsp/drc_math.h b/cras/src/dsp/drc_math.h
index 1cd0671a..eec2061b 100644
--- a/cras/src/dsp/drc_math.h
+++ b/cras/src/dsp/drc_math.h
@@ -12,7 +12,22 @@ extern "C" {
#include <stddef.h>
#include <math.h>
+
+#ifndef __BIONIC__
#include <ieee754.h>
+#else
+union ieee754_float
+{
+ float f;
+ /* Little endian float fields */
+ struct
+ {
+ unsigned int mantissa:23;
+ unsigned int exponent:8;
+ unsigned int negative:1;
+ } ieee;
+};
+#endif
/* Uncomment to use the slow but accurate functions. */
/* #define SLOW_DB_TO_LINEAR */
@@ -54,13 +69,13 @@ extern float db_to_linear[201]; /* from -100dB to 100dB */
void drc_math_init();
/* Rounds the input number to the nearest integer */
-#ifdef __arm__
+#if defined(__arm__)
static inline float round_int(float x)
{
return x < 0 ? (int)(x - 0.5f) : (int)(x + 0.5f);
}
#else
-#define round_int rintf /* glibc will use roundss if SSE4.1 is available */
+#define round_int rintf /* Uses frintx for arm64 and roundss for SSE4.1 */
#endif
static inline float decibels_to_linear(float decibels)
diff --git a/cras/src/dsp/dsp_util.c b/cras/src/dsp/dsp_util.c
index 6e99a6fb..6de9a4e7 100644
--- a/cras/src/dsp/dsp_util.c
+++ b/cras/src/dsp/dsp_util.c
@@ -1,9 +1,8 @@
-/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+/* Copyright 2013 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
-#include <fpu_control.h>
#include "dsp_util.h"
#ifndef max
@@ -21,6 +20,115 @@
#undef deinterleave_stereo
#undef interleave_stereo
+/* Converts shorts in range of -32768 to 32767 to floats in range of
+ * -1.0f to 1.0f.
+ * scvtf instruction accepts fixed point ints, so sxtl is used to lengthen
+ * shorts to int with sign extension.
+ */
+#ifdef __aarch64__
+static void deinterleave_stereo(int16_t *input, float *output1,
+ float *output2, int frames)
+{
+ int chunk = frames >> 3;
+ frames &= 7;
+ /* Process 8 frames (16 samples) each loop. */
+ /* L0 R0 L1 R1 L2 R2 L3 R3... -> L0 L1 L2 L3... R0 R1 R2 R3... */
+ if (chunk) {
+ __asm__ __volatile__ (
+ "1: \n"
+ "ld2 {v2.8h, v3.8h}, [%[input]], #32 \n"
+ "subs %w[chunk], %w[chunk], #1 \n"
+ "sxtl v0.4s, v2.4h \n"
+ "sxtl2 v1.4s, v2.8h \n"
+ "sxtl v2.4s, v3.4h \n"
+ "sxtl2 v3.4s, v3.8h \n"
+ "scvtf v0.4s, v0.4s, #15 \n"
+ "scvtf v1.4s, v1.4s, #15 \n"
+ "scvtf v2.4s, v2.4s, #15 \n"
+ "scvtf v3.4s, v3.4s, #15 \n"
+ "st1 {v0.4s, v1.4s}, [%[output1]], #32 \n"
+ "st1 {v2.4s, v3.4s}, [%[output2]], #32 \n"
+ "b.ne 1b \n"
+ : /* output */
+ [chunk]"+r"(chunk),
+ [input]"+r"(input),
+ [output1]"+r"(output1),
+ [output2]"+r"(output2)
+ : /* input */
+ : /* clobber */
+ "v0", "v1", "v2", "v3", "memory", "cc"
+ );
+ }
+
+ /* The remaining samples. */
+ while (frames--) {
+ *output1++ = *input++ / 32768.0f;
+ *output2++ = *input++ / 32768.0f;
+ }
+}
+#define deinterleave_stereo deinterleave_stereo
+
+/* Converts floats in range of -1.0f to 1.0f to shorts in range of
+ * -32768 to 32767 with rounding to nearest, with ties (0.5) rounding away
+ * from zero.
+ * Rounding is achieved by using fcvtas instruction. (a = away)
+ * The float scaled to a range of -32768 to 32767 by adding 15 to the exponent.
+ * Add to exponent is equivalent to multiply for exponent range of 0 to 239,
+ * which is 2.59 * 10^33. A signed saturating add (sqadd) limits exponents
+ * from 240 to 255 to clamp to 255.
+ * For very large values, beyond +/- 2 billion, fcvtas will clamp the result
+ * to the min or max value that fits an int.
+ * For other values, sqxtn clamps the output to -32768 to 32767 range.
+ */
+static void interleave_stereo(float *input1, float *input2,
+ int16_t *output, int frames)
+{
+ /* Process 4 frames (8 samples) each loop. */
+ /* L0 L1 L2 L3, R0 R1 R2 R3 -> L0 R0 L1 R1, L2 R2 L3 R3 */
+ int chunk = frames >> 2;
+ frames &= 3;
+
+ if (chunk) {
+ __asm__ __volatile__ (
+ "dup v2.4s, %w[scale] \n"
+ "1: \n"
+ "ld1 {v0.4s}, [%[input1]], #16 \n"
+ "ld1 {v1.4s}, [%[input2]], #16 \n"
+ "subs %w[chunk], %w[chunk], #1 \n"
+ "sqadd v0.4s, v0.4s, v2.4s \n"
+ "sqadd v1.4s, v1.4s, v2.4s \n"
+ "fcvtas v0.4s, v0.4s \n"
+ "fcvtas v1.4s, v1.4s \n"
+ "sqxtn v0.4h, v0.4s \n"
+ "sqxtn v1.4h, v1.4s \n"
+ "st2 {v0.4h, v1.4h}, [%[output]], #16 \n"
+ "b.ne 1b \n"
+ : /* output */
+ [chunk]"+r"(chunk),
+ [input1]"+r"(input1),
+ [input2]"+r"(input2),
+ [output]"+r"(output)
+ : /* input */
+ [scale]"r"(15 << 23)
+ : /* clobber */
+ "v0", "v1", "v2", "memory", "cc"
+ );
+ }
+
+ /* The remaining samples */
+ while (frames--) {
+ float f;
+ f = *input1++ * 32768.0f;
+ f += (f >= 0) ? 0.5f : -0.5f;
+ *output++ = max(-32768, min(32767, (int)(f)));
+ f = *input2++ * 32768.0f;
+ f += (f >= 0) ? 0.5f : -0.5f;
+ *output++ = max(-32768, min(32767, (int)(f)));
+ }
+}
+#define interleave_stereo interleave_stereo
+#endif
+
#ifdef __ARM_NEON__
#include <arm_neon.h>
@@ -66,6 +174,16 @@ static void deinterleave_stereo(int16_t *input, float *output1,
}
#define deinterleave_stereo deinterleave_stereo
+/* Converts floats in range of -1.0f to 1.0f to shorts in range of
+ * -32768 to 32767 with rounding to nearest, with ties (0.5) rounding away
+ * from zero.
+ * Rounding is achieved by adding 0.5 or -0.5 adjusted for fixed point
+ * precision, and then converting float to fixed point using vcvt instruction
+ * which truncated toward zero.
+ * For very large values, beyond +/- 2 billion, vcvt will clamp the result
+ * to the min or max value that fits an int.
+ * For other values, vqmovn clamps the output to -32768 to 32767 range.
+ */
static void interleave_stereo(float *input1, float *input2,
int16_t *output, int frames)
{
@@ -100,15 +218,11 @@ static void interleave_stereo(float *input1, float *input2,
"vst2.16 {d2-d3}, [%[output]]! \n"
"bne 1b \n"
: /* output */
- "=r"(chunk),
- "=r"(input1),
- "=r"(input2),
- "=r"(output)
+ [chunk]"+r"(chunk),
+ [input1]"+r"(input1),
+ [input2]"+r"(input2),
+ [output]"+r"(output)
: /* input */
- [chunk]"0"(chunk),
- [input1]"1"(input1),
- [input2]"2"(input2),
- [output]"3"(output),
[pos]"w"(pos),
[neg]"w"(neg)
: /* clobber */
@@ -119,21 +233,30 @@ static void interleave_stereo(float *input1, float *input2,
/* The remaining samples */
while (frames--) {
float f;
- f = *input1++;
- f += (f > 0) ? (0.5f / 32768.0f) : (-0.5f / 32768.0f);
- *output++ = max(-32768, min(32767, (int)(f * 32768.0f)));
- f = *input2++;
- f += (f > 0) ? (0.5f / 32768.0f) : (-0.5f / 32768.0f);
- *output++ = max(-32768, min(32767, (int)(f * 32768.0f)));
+ f = *input1++ * 32768.0f;
+ f += (f >= 0) ? 0.5f : -0.5f;
+ *output++ = max(-32768, min(32767, (int)(f)));
+ f = *input2++ * 32768.0f;
+ f += (f >= 0) ? 0.5f : -0.5f;
+ *output++ = max(-32768, min(32767, (int)(f)));
}
}
#define interleave_stereo interleave_stereo
-
#endif
#ifdef __SSE3__
#include <emmintrin.h>
+/* Converts shorts in range of -32768 to 32767 to floats in range of
+ * -1.0f to 1.0f.
+ * pslld and psrad shifts are used to isolate the low and high word, but
+ * each in a different range:
+ * The low word is shifted to the high bits in range 0x80000000 .. 0x7fff0000.
+ * The high word is shifted to the low bits in range 0x00008000 .. 0x00007fff.
+ * cvtdq2ps converts ints to floats as is.
+ * mulps is used to normalize the range of the low and high words, adjusting
+ * for high and low words being in different range.
+ */
static void deinterleave_stereo(int16_t *input, float *output1,
float *output2, int frames)
{
@@ -190,6 +313,12 @@ static void deinterleave_stereo(int16_t *input, float *output1,
}
#define deinterleave_stereo deinterleave_stereo
+/* Converts floats in range of -1.0f to 1.0f to shorts in range of
+ * -32768 to 32767 with rounding to nearest, with ties (0.5) rounding to
+ * even.
+ * For very large values, beyond +/- 2 billion, cvtps2dq will produce
+ * 0x80000000 and packssdw will clamp -32768.
+ */
static void interleave_stereo(float *input1, float *input2,
int16_t *output, int frames)
{
@@ -203,13 +332,13 @@ static void interleave_stereo(float *input1, float *input2,
"1: \n"
"lddqu (%[input1]), %%xmm0 \n"
"lddqu (%[input2]), %%xmm2 \n"
+ "add $16, %[input1] \n"
+ "add $16, %[input2] \n"
"movaps %%xmm0, %%xmm1 \n"
"unpcklps %%xmm2, %%xmm0 \n"
"unpckhps %%xmm2, %%xmm1 \n"
- "add $16, %[input1] \n"
- "add $16, %[input2] \n"
- "mulps %[scale_2_15], %%xmm0 \n"
- "mulps %[scale_2_15], %%xmm1 \n"
+ "paddsw %[scale_2_15], %%xmm0 \n"
+ "paddsw %[scale_2_15], %%xmm1 \n"
"cvtps2dq %%xmm0, %%xmm0 \n"
"cvtps2dq %%xmm1, %%xmm1 \n"
"packssdw %%xmm1, %%xmm0 \n"
@@ -218,16 +347,13 @@ static void interleave_stereo(float *input1, float *input2,
"sub $1, %[chunk] \n"
"jnz 1b \n"
: /* output */
- "=r"(chunk),
- "=r"(input1),
- "=r"(input2),
- "=r"(output)
+ [chunk]"+r"(chunk),
+ [input1]"+r"(input1),
+ [input2]"+r"(input2),
+ [output]"+r"(output)
: /* input */
- [chunk]"0"(chunk),
- [input1]"1"(input1),
- [input2]"2"(input2),
- [output]"3"(output),
- [scale_2_15]"x"(_mm_set1_ps(1.0f*(1<<15)))
+ [scale_2_15]"x"(_mm_set1_epi32(15 << 23)),
+ [clamp_large]"x"(_mm_set1_ps(32767.0f))
: /* clobber */
"xmm0", "xmm1", "xmm2", "memory", "cc"
);
@@ -236,16 +362,15 @@ static void interleave_stereo(float *input1, float *input2,
/* The remaining samples */
while (frames--) {
float f;
- f = *input1++;
- f += (f > 0) ? (0.5f / 32768.0f) : (-0.5f / 32768.0f);
- *output++ = max(-32768, min(32767, (int)(f * 32768.0f)));
- f = *input2++;
- f += (f > 0) ? (0.5f / 32768.0f) : (-0.5f / 32768.0f);
- *output++ = max(-32768, min(32767, (int)(f * 32768.0f)));
+ f = *input1++ * 32768.0f;
+ f += (f >= 0) ? 0.5f : -0.5f;
+ *output++ = max(-32768, min(32767, (int)(f)));
+ f = *input2++ * 32768.0f;
+ f += (f >= 0) ? 0.5f : -0.5f;
+ *output++ = max(-32768, min(32767, (int)(f)));
}
}
#define interleave_stereo interleave_stereo
-
#endif
void dsp_util_deinterleave(int16_t *input, float *const *output, int channels,
@@ -287,15 +412,9 @@ void dsp_util_interleave(float *const *input, int16_t *output, int channels,
for (i = 0; i < frames; i++)
for (j = 0; j < channels; j++) {
- int16_t i16;
float f = *(input_ptr[j]++) * 32768.0f;
- if (f > 32767)
- i16 = 32767;
- else if (f < -32768)
- i16 = -32768;
- else
- i16 = (int16_t) (f > 0 ? f + 0.5f : f - 0.5f);
- *output++ = i16;
+ f += (f >= 0) ? 0.5f : -0.5f;
+ *output++ = max(-32768, min(32767, (int)(f)));
}
}
@@ -305,10 +424,21 @@ void dsp_enable_flush_denormal_to_zero()
unsigned int mxcsr;
mxcsr = __builtin_ia32_stmxcsr();
__builtin_ia32_ldmxcsr(mxcsr | 0x8040);
+#elif defined(__aarch64__)
+ uint64_t cw;
+ __asm__ __volatile__ (
+ "mrs %0, fpcr \n"
+ "orr %0, %0, #0x1000000 \n"
+ "msr fpcr, %0 \n"
+ "isb \n"
+ : "=r"(cw) :: "memory");
#elif defined(__arm__)
- int cw;
- _FPU_GETCW(cw);
- _FPU_SETCW(cw | (1 << 24));
+ uint32_t cw;
+ __asm__ __volatile__ (
+ "vmrs %0, fpscr \n"
+ "orr %0, %0, #0x1000000 \n"
+ "vmsr fpscr, %0 \n"
+ : "=r"(cw) :: "memory");
#else
#warning "Don't know how to disable denorms. Performace may suffer."
#endif
diff --git a/cras/src/dsp/tests/dsp_util_test.c b/cras/src/dsp/tests/dsp_util_test.c
new file mode 100644
index 00000000..3a22db3f
--- /dev/null
+++ b/cras/src/dsp/tests/dsp_util_test.c
@@ -0,0 +1,376 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <math.h> /* for abs() */
+#include <stdio.h> /* for printf() */
+#include <string.h> /* for memset() */
+#include <stdint.h> /* for uint64 definition */
+#include <stdlib.h> /* for exit() definition */
+#include <time.h> /* for clock_gettime */
+
+#include "../drc_math.h"
+#include "../dsp_util.h"
+
+
+/* Constant for converting time to milliseconds. */
+#define BILLION 1000000000LL
+/* Number of iterations for performance testing. */
+#define ITERATIONS 400000
+
+#if defined(__aarch64__)
+int16_t float_to_short(float a) {
+ int32_t ret;
+ asm volatile ("fcvtas %s[ret], %s[a]\n"
+ "sqxtn %h[ret], %s[ret]\n"
+ : [ret] "=w" (ret)
+ : [a] "w" (a)
+ :);
+ return (int16_t)(ret);
+}
+#else
+int16_t float_to_short(float a) {
+ a += (a >= 0) ? 0.5f : -0.5f;
+ return (int16_t)(max(-32768, min(32767, a)));
+}
+#endif
+
+void dsp_util_deinterleave_reference(int16_t *input, float *const *output,
+ int channels, int frames)
+{
+ float *output_ptr[channels];
+ int i, j;
+
+ for (i = 0; i < channels; i++)
+ output_ptr[i] = output[i];
+
+ for (i = 0; i < frames; i++)
+ for (j = 0; j < channels; j++)
+ *(output_ptr[j]++) = *input++ / 32768.0f;
+}
+
+void dsp_util_interleave_reference(float *const *input, int16_t *output,
+ int channels, int frames)
+{
+ float *input_ptr[channels];
+ int i, j;
+
+ for (i = 0; i < channels; i++)
+ input_ptr[i] = input[i];
+
+ for (i = 0; i < frames; i++)
+ for (j = 0; j < channels; j++) {
+ float f = *(input_ptr[j]++) * 32768.0f;
+ *output++ = float_to_short(f);
+ }
+}
+
+/* Use fixed size allocation to avoid performance fluctuation of allocation. */
+#define MAXSAMPLES 4096
+#define MINSAMPLES 256
+/* PAD buffer to check for overflows. */
+#define PAD 4096
+
+void TestRounding(float in, int16_t expected, int samples)
+{
+ int i;
+ int max_diff;
+ int d;
+
+ short* in_shorts = (short*) malloc(MAXSAMPLES * 2 * 2 + PAD);
+ float* out_floats_left_c = (float*) malloc(MAXSAMPLES * 4 + PAD);
+ float* out_floats_right_c = (float*) malloc(MAXSAMPLES * 4 + PAD);
+ float* out_floats_left_opt = (float*) malloc(MAXSAMPLES * 4 + PAD);
+ float* out_floats_right_opt = (float*) malloc(MAXSAMPLES * 4 + PAD);
+ short* out_shorts_c = (short*) malloc(MAXSAMPLES * 2 * 2 + PAD);
+ short* out_shorts_opt = (short*) malloc(MAXSAMPLES * 2 * 2 + PAD);
+
+ memset(in_shorts, 0xfb, MAXSAMPLES * 2 * 2 + PAD);
+ memset(out_floats_left_c, 0xfb, MAXSAMPLES * 4 + PAD);
+ memset(out_floats_right_c, 0xfb, MAXSAMPLES * 4 + PAD);
+ memset(out_floats_left_opt, 0xfb, MAXSAMPLES * 4 + PAD);
+ memset(out_floats_right_opt, 0xfb, MAXSAMPLES * 4 + PAD);
+ memset(out_shorts_c, 0xfb, MAXSAMPLES * 2 * 2 + PAD);
+ memset(out_shorts_opt, 0xfb, MAXSAMPLES * 2 * 2 + PAD);
+
+ float *out_floats_ptr_c[2];
+ float *out_floats_ptr_opt[2];
+
+ out_floats_ptr_c[0] = out_floats_left_c;
+ out_floats_ptr_c[1] = out_floats_right_c;
+ out_floats_ptr_opt[0] = out_floats_left_opt;
+ out_floats_ptr_opt[1] = out_floats_right_opt;
+
+ for (i = 0; i < MAXSAMPLES; ++i) {
+ out_floats_left_c[i] = in;
+ out_floats_right_c[i] = in;
+ }
+
+ /* reference C interleave */
+ dsp_util_interleave_reference(out_floats_ptr_c, out_shorts_c, 2,
+ samples);
+
+ /* measure optimized interleave */
+ for (i = 0; i < ITERATIONS; ++i) {
+ dsp_util_interleave(out_floats_ptr_c, out_shorts_opt, 2,
+ samples);
+ }
+
+ max_diff = 0;
+ for (i = 0; i < (MAXSAMPLES * 2 + PAD / 2); ++i) {
+ d = abs(out_shorts_c[i] - out_shorts_opt[i]);
+ if (d > max_diff) {
+ max_diff = d;
+ }
+ }
+ printf("test interleave compare %6d, %10f %13f %6d %6d %6d %s\n",
+ max_diff, in, in * 32768.0f, out_shorts_c[0], out_shorts_opt[0],
+ expected,
+ max_diff == 0 ? "PASS" : (out_shorts_opt[0] == expected ?
+ "EXPECTED DIFFERENCE" : "UNEXPECTED DIFFERENCE"));
+
+ /* measure reference C deinterleave */
+ dsp_util_deinterleave_reference(in_shorts, out_floats_ptr_c, 2,
+ samples);
+
+ /* measure optimized deinterleave */
+ dsp_util_deinterleave(in_shorts, out_floats_ptr_opt, 2, samples);
+
+ d = memcmp(out_floats_ptr_c[0], out_floats_ptr_opt[0], samples * 4);
+ if (d) printf("left compare %d, %f %f\n", d, out_floats_ptr_c[0][0],
+ out_floats_ptr_opt[0][0]);
+ d = memcmp(out_floats_ptr_c[1], out_floats_ptr_opt[1], samples * 4);
+ if (d) printf("right compare %d, %f %f\n", d, out_floats_ptr_c[1][0],
+ out_floats_ptr_opt[1][0]);
+
+ free(in_shorts);
+ free(out_floats_left_c);
+ free(out_floats_right_c);
+ free(out_floats_left_opt);
+ free(out_floats_right_opt);
+ free(out_shorts_c);
+ free(out_shorts_opt);
+}
+
+int main(int argc, char **argv)
+{
+ float e = 0.000000001f;
+ int samples = 16;
+
+ dsp_enable_flush_denormal_to_zero();
+
+ // Print headings for TestRounding output.
+ printf("test interleave compare maxdif, float, float * 32k "
+ "C SIMD expect pass\n");
+
+ // test clamping
+ TestRounding(1.0f, 32767, samples);
+ TestRounding(-1.0f, -32768, samples);
+ TestRounding(1.1f, 32767, samples);
+ TestRounding(-1.1f, -32768, samples);
+ TestRounding(2000000000.f / 32768.f, 32767, samples);
+ TestRounding(-2000000000.f / 32768.f, -32768, samples);
+
+ /* Infinity produces zero on arm64. */
+#if defined(__aarch64__)
+#define EXPECTED_INF_RESULT 0
+#define EXPECTED_NEGINF_RESULT 0
+#elif defined(__i386__) || defined(__x86_64__)
+#define EXPECTED_INF_RESULT -32768
+#define EXPECTED_NEGINF_RESULT 0
+#else
+#define EXPECTED_INF_RESULT 32767
+#define EXPECTED_NEGINF_RESULT -32768
+#endif
+
+ TestRounding(5000000000.f / 32768.f, EXPECTED_INF_RESULT, samples);
+ TestRounding(-5000000000.f / 32768.f, EXPECTED_NEGINF_RESULT, samples);
+
+ // test infinity
+ union ieee754_float inf;
+ inf.ieee.negative = 0;
+ inf.ieee.exponent = 0xfe;
+ inf.ieee.mantissa = 0x7fffff;
+ TestRounding(inf.f, EXPECTED_INF_RESULT, samples); // expect fail
+ inf.ieee.negative = 1;
+ inf.ieee.exponent = 0xfe;
+ inf.ieee.mantissa = 0x7fffff;
+ TestRounding(inf.f, EXPECTED_NEGINF_RESULT, samples); // expect fail
+
+ // test rounding
+ TestRounding(0.25f, 8192, samples);
+ TestRounding(-0.25f, -8192, samples);
+ TestRounding(0.50f, 16384, samples);
+ TestRounding(-0.50f, -16384, samples);
+ TestRounding(1.0f / 32768.0f, 1, samples);
+ TestRounding(-1.0f / 32768.0f, -1, samples);
+ TestRounding(1.0f / 32768.0f + e, 1, samples);
+ TestRounding(-1.0f / 32768.0f - e, -1, samples);
+ TestRounding(1.0f / 32768.0f - e, 1, samples);
+ TestRounding(-1.0f / 32768.0f + e, -1, samples);
+
+ /* Rounding on 'tie' is different for Intel. */
+#if defined(__i386__) || defined(__x86_64__)
+ TestRounding(0.5f / 32768.0f, 0, samples); /* Expect round to even */
+ TestRounding(-0.5f / 32768.0f, 0, samples);
+#else
+ TestRounding(0.5f / 32768.0f, 1, samples); /* Expect round away */
+ TestRounding(-0.5f / 32768.0f, -1, samples);
+#endif
+
+ TestRounding(0.5f / 32768.0f + e, 1, samples);
+ TestRounding(-0.5f / 32768.0f - e, 1, samples);
+ TestRounding(0.5f / 32768.0f - e, 0, samples);
+ TestRounding(-0.5f / 32768.0f + e, 0, samples);
+
+ TestRounding(1.5f / 32768.0f, 2, samples);
+ TestRounding(-1.5f / 32768.0f, -2, samples);
+ TestRounding(1.5f / 32768.0f + e, 2, samples);
+ TestRounding(-1.5f / 32768.0f - e, -2, samples);
+ TestRounding(1.5f / 32768.0f - e, 1, samples);
+ TestRounding(-1.5f / 32768.0f + e, -1, samples);
+
+ /* Test denormals */
+ union ieee754_float denorm;
+ denorm.ieee.negative = 0;
+ denorm.ieee.exponent = 0;
+ denorm.ieee.mantissa = 1;
+ TestRounding(denorm.f, 0, samples);
+ denorm.ieee.negative = 1;
+ denorm.ieee.exponent = 0;
+ denorm.ieee.mantissa = 1;
+ TestRounding(denorm.f, 0, samples);
+
+ /* Test NaNs. Caveat Results vary by implementation. */
+#if defined(__i386__) || defined(__x86_64__)
+#define EXPECTED_NAN_RESULT -32768
+#else
+#define EXPECTED_NAN_RESULT 0
+#endif
+ union ieee754_float nan; /* Quiet NaN */
+ nan.ieee.negative = 0;
+ nan.ieee.exponent = 0xff;
+ nan.ieee.mantissa = 0x400001;
+ TestRounding(nan.f, EXPECTED_NAN_RESULT, samples);
+ nan.ieee.negative = 0;
+ nan.ieee.exponent = 0xff;
+ nan.ieee.mantissa = 0x000001; /* Signalling NaN */
+ TestRounding(nan.f, EXPECTED_NAN_RESULT, samples);
+
+ /* Test Performance */
+ uint64_t diff;
+ struct timespec start, end;
+ int i;
+ int d;
+
+ short* in_shorts = (short*) malloc(MAXSAMPLES * 2 * 2 + PAD);
+ float* out_floats_left_c = (float*) malloc(MAXSAMPLES * 4 + PAD);
+ float* out_floats_right_c = (float*) malloc(MAXSAMPLES * 4 + PAD);
+ float* out_floats_left_opt = (float*) malloc(MAXSAMPLES * 4 + PAD);
+ float* out_floats_right_opt = (float*) malloc(MAXSAMPLES * 4 + PAD);
+ short* out_shorts_c = (short*) malloc(MAXSAMPLES * 2 * 2 + PAD);
+ short* out_shorts_opt = (short*) malloc(MAXSAMPLES * 2 * 2 + PAD);
+
+ memset(in_shorts, 0x11, MAXSAMPLES * 2 * 2 + PAD);
+ memset(out_floats_left_c, 0x22, MAXSAMPLES * 4 + PAD);
+ memset(out_floats_right_c, 0x33, MAXSAMPLES * 4 + PAD);
+ memset(out_floats_left_opt, 0x44, MAXSAMPLES * 4 + PAD);
+ memset(out_floats_right_opt, 0x55, MAXSAMPLES * 4 + PAD);
+ memset(out_shorts_c, 0x66, MAXSAMPLES * 2 * 2 + PAD);
+ memset(out_shorts_opt, 0x66, MAXSAMPLES * 2 * 2 + PAD);
+
+ float *out_floats_ptr_c[2];
+ float *out_floats_ptr_opt[2];
+
+ out_floats_ptr_c[0] = out_floats_left_c;
+ out_floats_ptr_c[1] = out_floats_right_c;
+ out_floats_ptr_opt[0] = out_floats_left_opt;
+ out_floats_ptr_opt[1] = out_floats_right_opt;
+
+ /* Benchmark dsp_util_interleave */
+ for (samples = MAXSAMPLES; samples >= MINSAMPLES; samples /= 2) {
+
+ /* measure original C interleave */
+ clock_gettime(CLOCK_MONOTONIC, &start); /* mark start time */
+ for (i = 0; i < ITERATIONS; ++i) {
+ dsp_util_interleave_reference(out_floats_ptr_c,
+ out_shorts_c,
+ 2, samples);
+ }
+ clock_gettime(CLOCK_MONOTONIC, &end); /* mark the end time */
+ diff = (BILLION * (end.tv_sec - start.tv_sec) +
+ end.tv_nsec - start.tv_nsec) / 1000000;
+ printf("interleave ORIG size = %6d, elapsed time = %llu ms\n",
+ samples, (long long unsigned int) diff);
+
+ /* measure optimized interleave */
+ clock_gettime(CLOCK_MONOTONIC, &start); /* mark start time */
+ for (i = 0; i < ITERATIONS; ++i) {
+ dsp_util_interleave(out_floats_ptr_c, out_shorts_opt, 2,
+ samples);
+ }
+ clock_gettime(CLOCK_MONOTONIC, &end); /* mark the end time */
+ diff = (BILLION * (end.tv_sec - start.tv_sec) +
+ end.tv_nsec - start.tv_nsec) / 1000000;
+ printf("interleave SIMD size = %6d, elapsed time = %llu ms\n",
+ samples, (long long unsigned int) diff);
+
+ /* Test C and SIMD output match */
+ d = memcmp(out_shorts_c, out_shorts_opt,
+ MAXSAMPLES * 2 * 2 + PAD);
+ if (d) printf("interleave compare %d, %d %d, %d %d\n", d,
+ out_shorts_c[0], out_shorts_c[1],
+ out_shorts_opt[0], out_shorts_opt[1]);
+ }
+
+ /* Benchmark dsp_util_deinterleave */
+ for (samples = MAXSAMPLES; samples >= MINSAMPLES; samples /= 2) {
+
+ /* Measure original C deinterleave */
+ clock_gettime(CLOCK_MONOTONIC, &start); /* mark start time */
+ for (i = 0; i < ITERATIONS; ++i) {
+ dsp_util_deinterleave_reference(in_shorts,
+ out_floats_ptr_c,
+ 2, samples);
+ }
+ clock_gettime(CLOCK_MONOTONIC, &end); /* mark the end time */
+ diff = (BILLION * (end.tv_sec - start.tv_sec) +
+ end.tv_nsec - start.tv_nsec) / 1000000;
+ printf("deinterleave ORIG size = %6d, "
+ "elapsed time = %llu ms\n",
+ samples, (long long unsigned int) diff);
+
+ /* Measure optimized deinterleave */
+ clock_gettime(CLOCK_MONOTONIC, &start); /* mark start time */
+ for (i = 0; i < ITERATIONS; ++i) {
+ dsp_util_deinterleave(in_shorts, out_floats_ptr_opt, 2,
+ samples);
+ }
+ clock_gettime(CLOCK_MONOTONIC, &end); /* mark the end time */
+ diff = (BILLION * (end.tv_sec - start.tv_sec) +
+ end.tv_nsec - start.tv_nsec) / 1000000;
+ printf("deinterleave SIMD size = %6d, elapsed time = %llu ms\n",
+ samples, (long long unsigned int) diff);
+
+ /* Test C and SIMD output match */
+ d = memcmp(out_floats_ptr_c[0], out_floats_ptr_opt[0],
+ samples * 4);
+ if (d) printf("left compare %d, %f %f\n", d,
+ out_floats_ptr_c[0][0], out_floats_ptr_opt[0][0]);
+ d = memcmp(out_floats_ptr_c[1], out_floats_ptr_opt[1],
+ samples * 4);
+ if (d) printf("right compare %d, %f %f\n", d,
+ out_floats_ptr_c[1][0], out_floats_ptr_opt[1][0]);
+ }
+
+ free(in_shorts);
+ free(out_floats_left_c);
+ free(out_floats_right_c);
+ free(out_floats_left_opt);
+ free(out_floats_right_opt);
+ free(out_shorts_c);
+ free(out_shorts_opt);
+
+ return 0;
+} \ No newline at end of file
diff --git a/cras/src/libcras/cras_client.c b/cras/src/libcras/cras_client.c
index 8ff1ea48..98acde1f 100644
--- a/cras/src/libcras/cras_client.c
+++ b/cras/src/libcras/cras_client.c
@@ -30,12 +30,15 @@
#include <limits.h>
#include <poll.h>
#include <pthread.h>
+#include <stdbool.h>
#include <stdint.h>
+#include <sys/eventfd.h>
#include <sys/ipc.h>
+#include <sys/mman.h>
#include <sys/param.h>
-#include <sys/shm.h>
#include <sys/signal.h>
#include <sys/socket.h>
+#include <sys/timerfd.h>
#include <sys/types.h>
#include <sys/un.h>
#include <syslog.h>
@@ -43,27 +46,17 @@
#include "cras_client.h"
#include "cras_config.h"
+#include "cras_file_wait.h"
#include "cras_messages.h"
+#include "cras_observer_ops.h"
#include "cras_shm.h"
#include "cras_types.h"
#include "cras_util.h"
#include "utlist.h"
-#ifdef __ANDROID__
-void *shmat(int shmid, const void *shmaddr, int shmflg);
-int shmdt(const void *shmaddr);
-int shmget(key_t key, size_t size, int shmflg);
-
-long __set_errno_internal(int n) {
- errno = n;
- return -1;
-}
-#endif
-
static const size_t MAX_CMD_MSG_LEN = 256;
-static const size_t SERVER_CONNECT_TIMEOUT_NS = 500000000;
static const size_t SERVER_SHUTDOWN_TIMEOUT_US = 500000;
-static const size_t SERVER_FIRST_MESSAGE_TIMEOUT_NS = 500000000;
+static const size_t SERVER_CONNECT_TIMEOUT_MS = 1000;
/* Commands sent from the user to the running client. */
enum {
@@ -72,6 +65,7 @@ enum {
CLIENT_REMOVE_STREAM,
CLIENT_SET_STREAM_VOLUME_SCALER,
CLIENT_SERVER_CONNECT,
+ CLIENT_SERVER_CONNECT_ASYNC,
};
struct command_msg {
@@ -108,10 +102,17 @@ struct stream_msg {
cras_stream_id_t stream_id;
};
+enum CRAS_THREAD_STATE {
+ CRAS_THREAD_STOP, /* Isn't (shouldn't be) running. */
+ CRAS_THREAD_WARMUP, /* Is started, but not fully functional: waiting
+ * for resources to be ready for example. */
+ CRAS_THREAD_RUNNING, /* Is running and fully functional. */
+};
+
/* Manage information for a thread. */
struct thread_state {
pthread_t tid;
- unsigned running;
+ enum CRAS_THREAD_STATE state;
};
/* Parameters used when setting up a capture or playback stream. See comment
@@ -155,45 +156,180 @@ struct client_stream {
struct cras_client *client;
struct cras_stream_params *config;
struct cras_audio_shm capture_shm;
+ int capture_shm_size;
struct cras_audio_shm play_shm;
+ int play_shm_size;
struct client_stream *prev, *next;
};
+/* State of the socket. */
+typedef enum cras_socket_state {
+ CRAS_SOCKET_STATE_DISCONNECTED,
+ /* Not connected. Also used to cleanup the current connection
+ * before restarting the connection attempt. */
+ CRAS_SOCKET_STATE_WAIT_FOR_SOCKET,
+ /* Waiting for the socket file to exist. Socket file existence
+ * is monitored using cras_file_wait. */
+ CRAS_SOCKET_STATE_WAIT_FOR_WRITABLE,
+ /* Waiting for the socket to have something at the other end. */
+ CRAS_SOCKET_STATE_FIRST_MESSAGE,
+ /* Waiting for the first messages from the server and set our
+ * client ID. */
+ CRAS_SOCKET_STATE_CONNECTED,
+ /* The socket is connected and working. */
+ CRAS_SOCKET_STATE_ERROR_DELAY,
+ /* There was an error during one of the above states. Sleep for
+ * a bit before continuing. If this state could not be initiated
+ * then we move to the DISCONNECTED state and notify via the
+ * connection callback. */
+} cras_socket_state_t;
+
/* Represents a client used to communicate with the audio server.
* id - Unique identifier for this client, negative until connected.
- * server_fd Incoming messages from server.
+ * server_fd - Incoming messages from server.
+ * server_fd_state - State of the server's socket.
+ * server_event_fd - Eventfd to wait on until a connection is established.
* stream_fds - Pipe for attached streams.
* command_fds - Pipe for user commands to thread.
* command_reply_fds - Pipe for acking/nacking command messages from thread.
- * sock_dir - Directory where the local audio socket can be found.
+ * sock_file - Server communication socket file.
+ * sock_file_wait - Structure used to monitor existence of the socket file.
+ * sock_file_exists - Set to true when the socket file exists.
* running - The client thread will run while this is non zero.
* next_stream_id - ID to give the next stream.
+ * stream_start_cond - Condition used during stream startup.
+ * stream_start_lock - Lock used during stream startup.
* tid - Thread ID of the client thread started by "cras_client_run_thread".
* last_command_result - Passes back the result of the last user command.
* streams - Linked list of streams attached to this client.
* server_state - RO shared memory region holding server state.
* debug_info_callback - Function to call when debug info is received.
+ * get_hotword_models_cb_t - Function to call when hotword models info is ready.
+ * server_err_cb - Function to call when failed to read messages from server.
+ * server_err_user_arg - User argument for server_err_cb.
+ * server_connection_cb - Function to called when a connection state changes.
+ * server_connection_user_arg - User argument for server_connection_cb.
+ * thread_priority_cb - Function to call for setting audio thread priority.
+ * observer_ops - Functions to call when system state changes.
+ * observer_context - Context passed to client in state change callbacks.
*/
struct cras_client {
int id;
int server_fd;
+ cras_socket_state_t server_fd_state;
+ int server_event_fd;
int stream_fds[2];
int command_fds[2];
int command_reply_fds[2];
- const char *sock_dir;
+ const char *sock_file;
+ struct cras_file_wait *sock_file_wait;
+ bool sock_file_exists;
struct thread_state thread;
cras_stream_id_t next_stream_id;
+ pthread_cond_t stream_start_cond;
+ pthread_mutex_t stream_start_lock;
int last_command_result;
struct client_stream *streams;
const struct cras_server_state *server_state;
void (*debug_info_callback)(struct cras_client *);
+ get_hotword_models_cb_t get_hotword_models_cb;
+ cras_server_error_cb_t server_err_cb;
+ cras_connection_status_cb_t server_connection_cb;
+ void *server_connection_user_arg;
+ cras_thread_priority_cb_t thread_priority_cb;
+ struct cras_observer_ops observer_ops;
+ void *observer_context;
};
/*
+ * Holds the client pointer plus internal book keeping.
+ *
+ * client - The client
+ * server_state_rwlock - lock to make the client's server_state thread-safe.
+ */
+struct client_int {
+ struct cras_client client;
+ pthread_rwlock_t server_state_rwlock;
+};
+
+#define to_client_int(cptr) \
+((struct client_int *)((char *)cptr - offsetof(struct client_int, client)))
+
+/*
* Local Helpers
*/
+static int client_thread_rm_stream(struct cras_client *client,
+ cras_stream_id_t stream_id);
static int handle_message_from_server(struct cras_client *client);
+static int reregister_notifications(struct cras_client *client);
+
+/*
+ * Unlock the server_state_rwlock if lock_rc is 0.
+ *
+ * Args:
+ * client - The CRAS client pointer.
+ * lock_rc - The result of server_state_rdlock or
+ * server_state_wrlock.
+ */
+static void server_state_unlock(const struct cras_client *client,
+ int lock_rc)
+{
+ struct client_int *client_int;
+
+ if (!client)
+ return;
+ client_int = to_client_int(client);
+ if (lock_rc == 0)
+ pthread_rwlock_unlock(&client_int->server_state_rwlock);
+}
+
+/*
+ * Lock the server_state_rwlock for reading.
+ *
+ * Also checks that the server_state pointer is valid.
+ *
+ * Args:
+ * client - The CRAS client pointer.
+ * Returns:
+ * 0 for success, positive error code on error.
+ * Returns EINVAL if the server state pointer is NULL.
+ */
+static int server_state_rdlock(const struct cras_client *client)
+{
+ struct client_int *client_int;
+ int lock_rc;
+
+ if (!client)
+ return EINVAL;
+ client_int = to_client_int(client);
+ lock_rc = pthread_rwlock_rdlock(&client_int->server_state_rwlock);
+ if (lock_rc != 0)
+ return lock_rc;
+ if (!client->server_state) {
+ pthread_rwlock_unlock(&client_int->server_state_rwlock);
+ return EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Lock the server_state_rwlock for writing.
+ *
+ * Args:
+ * client - The CRAS client pointer.
+ * Returns:
+ * 0 for success, positive error code on error.
+ */
+static int server_state_wrlock(const struct cras_client *client)
+{
+ struct client_int *client_int;
+
+ if (!client)
+ return EINVAL;
+ client_int = to_client_int(client);
+ return pthread_rwlock_wrlock(&client_int->server_state_rwlock);
+}
/* Get the stream pointer from a stream id. */
static struct client_stream *stream_from_id(const struct cras_client *client,
@@ -205,129 +341,610 @@ static struct client_stream *stream_from_id(const struct cras_client *client,
return out;
}
-/* Waits until we have heard back from the server so that we know we are
- * connected. The connected success/failure message is always the first message
- * the server sends. Return non zero if client is connected to the server. A
- * return code of zero means that the client is not connected to the server. */
-static int check_server_connected_wait(struct cras_client *client)
+/*
+ * Fill a pollfd structure with the current server fd and events.
+ */
+void server_fill_pollfd(const struct cras_client *client,
+ struct pollfd *poll_fd)
{
- struct pollfd pollfd;
- int rc;
- struct timespec timeout, now;
+ int events = 0;
- clock_gettime(CLOCK_MONOTONIC_RAW, &now);
- timeout.tv_sec = 0;
- timeout.tv_nsec = SERVER_FIRST_MESSAGE_TIMEOUT_NS;
- add_timespecs(&timeout, &now);
-
- pollfd.fd = client->server_fd;
- pollfd.events = POLLIN;
-
- while (timespec_after(&timeout, &now) > 0 && client->id < 0) {
- rc = ppoll(&pollfd, 1, &timeout, NULL);
- if (rc <= 0 && rc != -EAGAIN)
- return 0; /* Timeout or error. */
- if (pollfd.revents & POLLIN) {
- rc = handle_message_from_server(client);
- if (rc < 0)
- return 0;
- }
- clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+ poll_fd->fd = client->server_fd;
+ switch (client->server_fd_state) {
+ case CRAS_SOCKET_STATE_DISCONNECTED:
+ break;
+ case CRAS_SOCKET_STATE_WAIT_FOR_SOCKET:
+ case CRAS_SOCKET_STATE_FIRST_MESSAGE:
+ case CRAS_SOCKET_STATE_CONNECTED:
+ case CRAS_SOCKET_STATE_ERROR_DELAY:
+ events = POLLIN;
+ break;
+ case CRAS_SOCKET_STATE_WAIT_FOR_WRITABLE:
+ events = POLLOUT;
+ break;
}
+ poll_fd->events = events;
+ poll_fd->revents = 0;
+}
+
+/*
+ * Change the server_fd_state.
+ */
+static void server_fd_move_to_state(struct cras_client *client,
+ cras_socket_state_t state)
+{
+ if (state == client->server_fd_state)
+ return;
- return client->id >= 0;
+ client->server_fd_state = state;
}
-/* Waits until the fd is writable or the specified time has passed. Returns 0 if
- * the fd is writable, -1 for timeout or other error. */
-static int wait_until_fd_writable(int fd, int timeout_ns)
+/*
+ * Action to take when in state ERROR_DELAY.
+ *
+ * In this state we want to sleep for a few seconds before retrying the
+ * connection to the audio server.
+ *
+ * If server_fd is negative: create a timer and setup server_fd with the
+ * timer's fd. If server_fd is not negative and there is input, then assume
+ * that the timer has expired, and restart the connection by moving to
+ * WAIT_FOR_SOCKET state.
+ */
+static int error_delay_next_action(struct cras_client *client,
+ int poll_revents)
{
- struct pollfd pollfd;
- struct timespec timeout;
int rc;
+ struct itimerspec timeout;
+
+ if (client->server_fd == -1) {
+ client->server_fd = timerfd_create(
+ CLOCK_MONOTONIC,
+ TFD_NONBLOCK|TFD_CLOEXEC);
+ if (client->server_fd == -1) {
+ rc = -errno;
+ syslog(LOG_ERR,
+ "cras_client: Could not create timerfd: %s",
+ strerror(-rc));
+ return rc;
+ }
- timeout.tv_sec = 0;
- timeout.tv_nsec = timeout_ns;
-
- pollfd.fd = fd;
- pollfd.events = POLLOUT;
+ /* Setup a relative timeout of 2 seconds. */
+ memset(&timeout, 0, sizeof(timeout));
+ timeout.it_value.tv_sec = 2;
+ rc = timerfd_settime(client->server_fd, 0, &timeout, NULL);
+ if (rc != 0) {
+ rc = -errno;
+ syslog(LOG_ERR,
+ "cras_client: Could not set timeout: %s",
+ strerror(-rc));
+ return rc;
+ }
+ return 0;
+ } else if ((poll_revents & POLLIN) == 0) {
+ return 0;
+ }
- rc = ppoll(&pollfd, 1, &timeout, NULL);
- if (rc <= 0)
- return -1;
+ /* Move to the next state: close the timer fd first. */
+ close(client->server_fd);
+ client->server_fd = -1;
+ server_fd_move_to_state(client, CRAS_SOCKET_STATE_WAIT_FOR_SOCKET);
return 0;
}
-/* Opens the server socket and connects to it. */
-static int connect_to_server(struct cras_client *client)
+/*
+ * Action to take when in WAIT_FOR_SOCKET state.
+ *
+ * In this state we are waiting for the socket file to exist. The existence of
+ * the socket file is continually monitored using the cras_file_wait structure
+ * and a separate fd. When the sock_file_exists boolean is modified, the state
+ * machine is invoked.
+ *
+ * If the socket file exists, then we move to the WAIT_FOR_WRITABLE state.
+ */
+static void wait_for_socket_next_action(struct cras_client *client)
+{
+ if (client->sock_file_exists)
+ server_fd_move_to_state(
+ client, CRAS_SOCKET_STATE_WAIT_FOR_WRITABLE);
+}
+
+/*
+ * Action to take when in WAIT_FOR_WRITABLE state.
+ *
+ * In this state we are initiating a connection the server and waiting for the
+ * server to ready for incoming messages.
+ *
+ * Create the socket to the server, and wait while a connect request results in
+ * -EINPROGRESS. Otherwise, we assume that the socket file will be deleted by
+ * the server and the server_fd_state will be changed in
+ * sock_file_wait_dispatch().
+ */
+static int wait_for_writable_next_action(struct cras_client *client,
+ int poll_revents)
{
int rc;
struct sockaddr_un address;
- if (client->server_fd >= 0)
- close(client->server_fd);
- client->server_fd = socket(PF_UNIX, SOCK_SEQPACKET, 0);
- if (client->server_fd < 0) {
- syslog(LOG_ERR, "cras_client: %s: Socket failed.", __func__);
- return client->server_fd;
+ if (client->server_fd == -1) {
+ client->server_fd = socket(PF_UNIX, SOCK_SEQPACKET, 0);
+ if (client->server_fd < 0) {
+ rc = -errno;
+ syslog(LOG_ERR, "cras_client: server socket failed: %s",
+ strerror(-rc));
+ return rc;
+ }
+ }
+ else if ((poll_revents & POLLOUT) == 0) {
+ return 0;
}
-
- memset(&address, 0, sizeof(struct sockaddr_un));
-
- address.sun_family = AF_UNIX;
- client->sock_dir = cras_config_get_system_socket_file_dir();
- assert(client->sock_dir);
- snprintf(address.sun_path, sizeof(address.sun_path),
- "%s/%s", client->sock_dir, CRAS_SOCKET_FILE);
/* We make the file descriptor non-blocking when we do connect(), so we
- * don't block indifinitely. */
+ * don't block indefinitely. */
cras_make_fd_nonblocking(client->server_fd);
+
+ memset(&address, 0, sizeof(struct sockaddr_un));
+ address.sun_family = AF_UNIX;
+ strcpy(address.sun_path, client->sock_file);
rc = connect(client->server_fd, (struct sockaddr *)&address,
sizeof(struct sockaddr_un));
-
- if (rc == -1 && errno == EINPROGRESS) {
- rc = wait_until_fd_writable(client->server_fd,
- SERVER_CONNECT_TIMEOUT_NS);
+ if (rc != 0) {
+ rc = -errno;
+ /* For -EINPROGRESS, we wait for POLLOUT on the server_fd.
+ * Otherwise CRAS is not running and we assume that the socket
+ * file will be deleted and recreated. Notification of that will
+ * happen via the sock_file_wait_dispatch(). */
+ if (rc == -ECONNREFUSED) {
+ /* CRAS is not running, don't log this error and just
+ * stay in this state waiting sock_file_wait_dispatch()
+ * to move the state machine. */
+ close(client->server_fd);
+ client->server_fd = -1;
+ }
+ else if (rc != -EINPROGRESS) {
+ syslog(LOG_ERR,
+ "cras_client: server connect failed: %s",
+ strerror(-rc));
+ return rc;
+ }
+ return 0;
}
cras_make_fd_blocking(client->server_fd);
+ server_fd_move_to_state(client, CRAS_SOCKET_STATE_FIRST_MESSAGE);
+ return 0;
+}
- if (rc != 0) {
- close(client->server_fd);
+/*
+ * Action to take when transitioning to the CONNECTED state.
+ */
+static int connect_transition_action(struct cras_client *client)
+{
+ eventfd_t event_value;
+ int rc;
+
+ rc = reregister_notifications(client);
+ if (rc < 0)
+ return rc;
+
+ server_fd_move_to_state(client, CRAS_SOCKET_STATE_CONNECTED);
+ /* Notify anyone waiting on this state change that we're
+ * connected. */
+ eventfd_read(client->server_event_fd, &event_value);
+ eventfd_write(client->server_event_fd, 1);
+ if (client->server_connection_cb)
+ client->server_connection_cb(
+ client, CRAS_CONN_STATUS_CONNECTED,
+ client->server_connection_user_arg);
+ return 0;
+}
+
+/*
+ * Action to take when in the FIRST_MESSAGE state.
+ *
+ * We are waiting for the first message from the server. When our client ID has
+ * been set, then we can move to the CONNECTED state.
+ */
+static int first_message_next_action(struct cras_client *client,
+ int poll_revents)
+{
+ int rc;
+
+ if (client->server_fd < 0)
+ return -EINVAL;
+
+ if ((poll_revents & POLLIN) == 0)
+ return 0;
+
+ rc = handle_message_from_server(client);
+ if (rc < 0) {
+ syslog(LOG_ERR, "handle first message: %s", strerror(-rc));
+ } else if (client->id >= 0) {
+ rc = connect_transition_action(client);
+ } else {
+ syslog(LOG_ERR, "did not get ID after first message!");
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+/*
+ * Play nice and shutdown the server socket.
+ */
+static inline int shutdown_and_close_socket(int sockfd)
+{
+ int rc;
+ uint8_t buffer[CRAS_CLIENT_MAX_MSG_SIZE];
+ struct timeval tv;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = SERVER_SHUTDOWN_TIMEOUT_US;
+ setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
+
+ rc = shutdown(sockfd, SHUT_WR);
+ if (rc < 0)
+ return rc;
+ /* Wait until the socket is closed by the peer. */
+ for (;;) {
+ rc = recv(sockfd, buffer, sizeof(buffer), 0);
+ if (rc <= 0)
+ break;
+ }
+ return close(sockfd);
+}
+
+/*
+ * Action to take when disconnecting from the server.
+ *
+ * Clean up the server socket, and the server_state pointer. Move to the next
+ * logical state.
+ */
+static void disconnect_transition_action(struct cras_client *client, bool force)
+{
+ eventfd_t event_value;
+ cras_socket_state_t old_state = client->server_fd_state;
+ struct client_stream *s;
+ int lock_rc;
+
+ /* Stop all playing streams.
+ * TODO(muirj): Pause and resume streams. */
+ DL_FOREACH(client->streams, s) {
+ s->config->err_cb(client, s->id, -ENOTCONN,
+ s->config->user_data);
+ client_thread_rm_stream(client, s->id);
+ }
+
+ /* Clean up the server_state pointer. */
+ lock_rc = server_state_wrlock(client);
+ if (client->server_state) {
+ munmap((void *)client->server_state,
+ sizeof(*client->server_state));
+ client->server_state = NULL;
+ }
+ server_state_unlock(client, lock_rc);
+
+ /* Our ID is unknown now. */
+ client->id = -1;
+
+ /* Clean up the server fd. */
+ if (client->server_fd >= 0) {
+ if (!force)
+ shutdown_and_close_socket(client->server_fd);
+ else
+ close(client->server_fd);
client->server_fd = -1;
- syslog(LOG_ERR, "cras_client: %s: Connect server failed.",
- __func__);
}
+ /* Reset the server_event_fd value to 0 (and cause subsequent threads
+ * waiting on the connection to wait). */
+ eventfd_read(client->server_event_fd, &event_value);
+
+ switch (old_state) {
+ case CRAS_SOCKET_STATE_DISCONNECTED:
+ /* Do nothing: already disconnected. */
+ break;
+ case CRAS_SOCKET_STATE_ERROR_DELAY:
+ /* We're disconnected and there was a failure to setup
+ * automatic reconnection, so call the server error
+ * callback now. */
+ server_fd_move_to_state(
+ client, CRAS_SOCKET_STATE_DISCONNECTED);
+ if (client->server_connection_cb)
+ client->server_connection_cb(
+ client, CRAS_CONN_STATUS_FAILED,
+ client->server_connection_user_arg);
+ else if (client->server_err_cb)
+ client->server_err_cb(
+ client, client->server_connection_user_arg);
+ break;
+ case CRAS_SOCKET_STATE_WAIT_FOR_SOCKET:
+ case CRAS_SOCKET_STATE_WAIT_FOR_WRITABLE:
+ case CRAS_SOCKET_STATE_FIRST_MESSAGE:
+ /* We are running this state transition while a connection is
+ * in progress for an error case. When there is no error, we
+ * come into this function in the DISCONNECTED state. */
+ server_fd_move_to_state(
+ client, CRAS_SOCKET_STATE_ERROR_DELAY);
+ break;
+ case CRAS_SOCKET_STATE_CONNECTED:
+ /* Disconnected from CRAS (for an error), wait for the socket
+ * file to be (re)created. */
+ server_fd_move_to_state(
+ client, CRAS_SOCKET_STATE_WAIT_FOR_SOCKET);
+ /* Notify the caller that we aren't connected anymore. */
+ if (client->server_connection_cb)
+ client->server_connection_cb(
+ client, CRAS_CONN_STATUS_DISCONNECTED,
+ client->server_connection_user_arg);
+ break;
+ }
+}
+
+static int server_fd_dispatch(struct cras_client *client, int poll_revents)
+{
+ int rc = 0;
+ cras_socket_state_t old_state;
+
+ if ((poll_revents & POLLHUP) != 0) {
+ /* Error or disconnect: cleanup and make a state change now. */
+ disconnect_transition_action(client, true);
+ }
+ old_state = client->server_fd_state;
+
+ switch (client->server_fd_state) {
+ case CRAS_SOCKET_STATE_DISCONNECTED:
+ /* Assume that we've taken the necessary actions. */
+ return -ENOTCONN;
+ case CRAS_SOCKET_STATE_ERROR_DELAY:
+ rc = error_delay_next_action(client, poll_revents);
+ break;
+ case CRAS_SOCKET_STATE_WAIT_FOR_SOCKET:
+ wait_for_socket_next_action(client);
+ break;
+ case CRAS_SOCKET_STATE_WAIT_FOR_WRITABLE:
+ rc = wait_for_writable_next_action(client, poll_revents);
+ break;
+ case CRAS_SOCKET_STATE_FIRST_MESSAGE:
+ rc = first_message_next_action(client, poll_revents);
+ break;
+ case CRAS_SOCKET_STATE_CONNECTED:
+ if ((poll_revents & POLLIN) != 0)
+ rc = handle_message_from_server(client);
+ break;
+ }
+
+ if (rc != 0) {
+ /* If there is an error, then start-over. */
+ rc = server_fd_dispatch(client, POLLHUP);
+ } else if (old_state != client->server_fd_state) {
+ /* There was a state change, process the new state now. */
+ rc = server_fd_dispatch(client, 0);
+ }
return rc;
}
-/* Tries to connect to the server. Waits for the initial message from the
- * server. This will happen near instantaneously if the server is already
- * running.*/
-static int connect_to_server_wait(struct cras_client *client)
+/*
+ * Start connecting to the server if we aren't already.
+ */
+static int server_connect(struct cras_client *client)
{
- unsigned int retries = 4;
- const unsigned int retry_delay_ms = 200;
+ if (client->server_fd_state != CRAS_SOCKET_STATE_DISCONNECTED)
+ return 0;
+ /* Start waiting for the server socket to exist. */
+ server_fd_move_to_state(client, CRAS_SOCKET_STATE_WAIT_FOR_SOCKET);
+ return server_fd_dispatch(client, 0);
+}
- assert(client);
+/*
+ * Disconnect from the server if we haven't already.
+ */
+static void server_disconnect(struct cras_client *client)
+{
+ if (client->server_fd_state == CRAS_SOCKET_STATE_DISCONNECTED)
+ return;
+ /* Set the disconnected state first so that the disconnect
+ * transition doesn't move the server state to ERROR_DELAY. */
+ server_fd_move_to_state(client, CRAS_SOCKET_STATE_DISCONNECTED);
+ disconnect_transition_action(client, false);
+}
- /* Ignore sig pipe as it will be handled when we write to the socket. */
- signal(SIGPIPE, SIG_IGN);
+/*
+ * Called when something happens to the socket file.
+ */
+static void sock_file_wait_callback(void *context, cras_file_wait_event_t event,
+ const char *filename)
+{
+ struct cras_client *client = (struct cras_client *)context;
+ switch (event) {
+ case CRAS_FILE_WAIT_EVENT_CREATED:
+ client->sock_file_exists = 1;
+ switch (client->server_fd_state) {
+ case CRAS_SOCKET_STATE_DISCONNECTED:
+ case CRAS_SOCKET_STATE_ERROR_DELAY:
+ case CRAS_SOCKET_STATE_FIRST_MESSAGE:
+ case CRAS_SOCKET_STATE_CONNECTED:
+ break;
+ case CRAS_SOCKET_STATE_WAIT_FOR_SOCKET:
+ case CRAS_SOCKET_STATE_WAIT_FOR_WRITABLE:
+ /* The socket file exists. Tell the server state
+ * machine. */
+ server_fd_dispatch(client, 0);
+ break;
+ }
+ break;
+ case CRAS_FILE_WAIT_EVENT_DELETED:
+ client->sock_file_exists = 0;
+ switch (client->server_fd_state) {
+ case CRAS_SOCKET_STATE_DISCONNECTED:
+ break;
+ case CRAS_SOCKET_STATE_WAIT_FOR_SOCKET:
+ case CRAS_SOCKET_STATE_WAIT_FOR_WRITABLE:
+ case CRAS_SOCKET_STATE_ERROR_DELAY:
+ case CRAS_SOCKET_STATE_FIRST_MESSAGE:
+ case CRAS_SOCKET_STATE_CONNECTED:
+ /* Restart the connection process. */
+ server_disconnect(client);
+ server_connect(client);
+ break;
+ }
+ break;
+ case CRAS_FILE_WAIT_EVENT_NONE:
+ break;
+ }
+}
+
+/*
+ * Service the sock_file_wait's fd.
+ *
+ * If the socket file is deleted, then cause a disconnect from the server.
+ * Otherwise, start a reconnect depending on the server_fd_state.
+ */
+static int sock_file_wait_dispatch(struct cras_client *client,
+ int poll_revents)
+{
+ int rc;
+
+ if ((poll_revents & POLLIN) == 0)
+ return 0;
+
+ rc = cras_file_wait_dispatch(client->sock_file_wait);
+ if (rc == -EAGAIN || rc == -EWOULDBLOCK)
+ rc = 0;
+ else if (rc != 0)
+ syslog(LOG_ERR, "cras_file_wait_dispatch: %s", strerror(-rc));
+ return rc;
+}
+
+/*
+ * Waits until we have heard back from the server so that we know we are
+ * connected.
+ *
+ * The connected success/failure message is always the first message the server
+ * sends. Return non zero if client is connected to the server. A return code
+ * of zero means that the client is not connected to the server.
+ */
+static int check_server_connected_wait(struct cras_client *client,
+ struct timespec *timeout)
+{
+ int rc = 0;
+ struct pollfd poll_fd;
- while (--retries) {
- /* If connected, wait for the first message from the server
- * indicating it's ready. */
- if (connect_to_server(client) == 0 &&
- check_server_connected_wait(client))
- return 0;
+ poll_fd.fd = client->server_event_fd;
+ poll_fd.events = POLLIN;
+ poll_fd.revents = 0;
- /* If we didn't succeed, wait and try again. */
- usleep(retry_delay_ms * 1000);
+ /* The server_event_fd is only read and written by the functions
+ * that connect to the server. When a connection is established the
+ * eventfd has a value of 1 and cras_poll will return immediately
+ * with 1. When there is no connection to the server, then this
+ * function waits until the timeout has expired or a non-zero value
+ * is written to the server_event_fd. */
+ while (rc == 0)
+ rc = cras_poll(&poll_fd, 1, timeout, NULL);
+ return rc > 0;
+}
+
+/* Returns non-zero if the thread is running (not stopped). */
+static inline int thread_is_running(struct thread_state *thread)
+{
+ return thread->state != CRAS_THREAD_STOP;
+}
+
+/*
+ * Opens the server socket and connects to it.
+ * Args:
+ * client - Client pointer created with cras_client_create().
+ * timeout - Connection timeout.
+ * Returns:
+ * 0 for success, negative error code on failure.
+ */
+static int connect_to_server(struct cras_client *client,
+ struct timespec *timeout,
+ bool use_command_thread)
+{
+ int rc;
+ struct pollfd poll_fd[2];
+ struct timespec connected_timeout;
+
+ if (!client)
+ return -EINVAL;
+
+ if (thread_is_running(&client->thread) && use_command_thread) {
+ rc = cras_client_connect_async(client);
+ if (rc == 0) {
+ rc = check_server_connected_wait(client, timeout);
+ return rc ? 0 : -ESHUTDOWN;
+ }
}
- return -EIO;
+ connected_timeout.tv_sec = 0;
+ connected_timeout.tv_nsec = 0;
+ if (check_server_connected_wait(client, &connected_timeout))
+ return 0;
+
+ poll_fd[0].fd = cras_file_wait_get_fd(client->sock_file_wait);
+ poll_fd[0].events = POLLIN;
+
+ rc = server_connect(client);
+ while(rc == 0) {
+ // Wait until we've connected or until there is a timeout.
+ // Meanwhile handle incoming actions on our fds.
+
+ server_fill_pollfd(client, &(poll_fd[1]));
+ rc = cras_poll(poll_fd, 2, timeout, NULL);
+ if (rc <= 0)
+ continue;
+
+ if (poll_fd[0].revents) {
+ rc = sock_file_wait_dispatch(
+ client, poll_fd[0].revents);
+ continue;
+ }
+
+ if (poll_fd[1].revents) {
+ rc = server_fd_dispatch(client, poll_fd[1].revents);
+ if (rc == 0 &&
+ client->server_fd_state ==
+ CRAS_SOCKET_STATE_CONNECTED)
+ break;
+ }
+ }
+
+ if (rc != 0)
+ syslog(LOG_ERR, "cras_client: Connect server failed: %s",
+ strerror(-rc));
+
+ return rc;
+}
+
+static int connect_to_server_wait_retry(struct cras_client *client,
+ int timeout_ms,
+ bool use_command_thread)
+{
+ struct timespec timeout_value;
+ struct timespec *timeout;
+
+ if (timeout_ms < 0) {
+ timeout = NULL;
+ } else {
+ timeout = &timeout_value;
+ ms_to_timespec(timeout_ms, timeout);
+ }
+
+ /* If connected, wait for the first message from the server
+ * indicating it's ready. */
+ return connect_to_server(client, timeout, use_command_thread);
+}
+
+/*
+ * Tries to connect to the server. Waits for the initial message from the
+ * server. This will happen near instantaneously if the server is already
+ * running.
+ */
+static int connect_to_server_wait(struct cras_client *client,
+ bool use_command_thread)
+{
+ return connect_to_server_wait_retry(
+ client, SERVER_CONNECT_TIMEOUT_MS, use_command_thread);
}
/*
@@ -359,23 +976,27 @@ static int read_with_wake_fd(int wake_fd, int read_fd, uint8_t *buf, size_t len)
{
struct pollfd pollfds[2];
int nread = 0;
+ int nfds = 1;
int rc;
char tmp;
- pollfds[0].fd = read_fd;
+ pollfds[0].fd = wake_fd;
pollfds[0].events = POLLIN;
- pollfds[1].fd = wake_fd;
- pollfds[1].events = POLLIN;
+ if (read_fd >= 0) {
+ nfds++;
+ pollfds[1].fd = read_fd;
+ pollfds[1].events = POLLIN;
+ }
- rc = poll(pollfds, 2, -1);
+ rc = poll(pollfds, nfds, -1);
if (rc < 0)
return rc;
- if (pollfds[0].revents & POLLIN) {
+ if (read_fd >= 0 && pollfds[1].revents & POLLIN) {
nread = read(read_fd, buf, len);
if (nread != (int)len)
return -EIO;
}
- if (pollfds[1].revents & POLLIN) {
+ if (pollfds[0].revents & POLLIN) {
rc = read(wake_fd, &tmp, 1);
if (rc < 0)
return rc;
@@ -383,14 +1004,22 @@ static int read_with_wake_fd(int wake_fd, int read_fd, uint8_t *buf, size_t len)
return nread;
}
-
-/* Check if doing format conversion and configure a capture buffer appropriately
- * before passing to the client. */
+/* Check the availability and configures a capture buffer.
+ * Args:
+ * stream - The input stream to configure buffer for.
+ * captured_frames - To be filled with the pointer to the beginning of
+ * captured buffer.
+ * num_frames - Number of captured frames.
+ * Returns:
+ * Number of frames available in captured_frames.
+ */
static unsigned int config_capture_buf(struct client_stream *stream,
uint8_t **captured_frames,
unsigned int num_frames)
{
- *captured_frames = cras_shm_get_curr_read_buffer(&stream->capture_shm);
+ /* Always return the beginning of the read buffer because Chrome expects
+ * so. */
+ *captured_frames = cras_shm_get_read_buffer_base(&stream->capture_shm);
/* Don't ask for more frames than the client desires. */
if (stream->flags & BULK_AUDIO_OK)
@@ -398,6 +1027,12 @@ static unsigned int config_capture_buf(struct client_stream *stream,
else
num_frames = MIN(num_frames, stream->config->cb_threshold);
+ /* If shm readable frames is less than client requests, that means
+ * overrun has happened in server side. Don't send partial corrupted
+ * buffer to client. */
+ if (cras_shm_get_curr_read_frames(&stream->capture_shm) < num_frames)
+ return 0;
+
return num_frames;
}
@@ -432,6 +1067,9 @@ static int handle_capture_data_ready(struct client_stream *stream,
}
num_frames = config_capture_buf(stream, &captured_frames, num_frames);
+ if (num_frames == 0)
+ return 0;
+
cras_timespec_to_timespec(&ts, &stream->capture_shm.area->ts);
if (config->unified_cb)
@@ -542,6 +1180,20 @@ reply_written:
return rc;
}
+static void audio_thread_set_priority(struct client_stream *stream)
+{
+ /* Use provided callback to set priority if available. */
+ if (stream->client->thread_priority_cb) {
+ stream->client->thread_priority_cb(stream->client);
+ return;
+ }
+
+ /* Try to get RT scheduling, if that fails try to set the nice value. */
+ if (cras_set_rt_scheduling(CRAS_CLIENT_RT_THREAD_PRIORITY) ||
+ cras_set_thread_priority(CRAS_CLIENT_RT_THREAD_PRIORITY))
+ cras_set_nice_level(CRAS_CLIENT_NICENESS_LEVEL);
+}
+
/* Listens to the audio socket for messages from the server indicating that
* the stream needs to be serviced. One of these runs per stream. */
static void *audio_thread(void *arg)
@@ -549,19 +1201,26 @@ static void *audio_thread(void *arg)
struct client_stream *stream = (struct client_stream *)arg;
int thread_terminated = 0;
struct audio_message aud_msg;
+ int aud_fd;
int num_read;
if (arg == NULL)
return (void *)-EIO;
- /* Try to get RT scheduling, if that fails try to set the nice value. */
- if (cras_set_rt_scheduling(CRAS_CLIENT_RT_THREAD_PRIORITY) ||
- cras_set_thread_priority(CRAS_CLIENT_RT_THREAD_PRIORITY))
- cras_set_nice_level(CRAS_CLIENT_NICENESS_LEVEL);
+ audio_thread_set_priority(stream);
+
+ /* Notify the control thread that we've started. */
+ pthread_mutex_lock(&stream->client->stream_start_lock);
+ pthread_cond_broadcast(&stream->client->stream_start_cond);
+ pthread_mutex_unlock(&stream->client->stream_start_lock);
- while (stream->thread.running && !thread_terminated) {
+ while (thread_is_running(&stream->thread) && !thread_terminated) {
+ /* While we are warming up, aud_fd may not be valid and some
+ * shared memory resources may not yet be available. */
+ aud_fd = (stream->thread.state == CRAS_THREAD_WARMUP) ?
+ -1 : stream->aud_fd;
num_read = read_with_wake_fd(stream->wake_fds[0],
- stream->aud_fd,
+ aud_fd,
(uint8_t *)&aud_msg,
sizeof(aud_msg));
if (num_read < 0)
@@ -599,6 +1258,77 @@ static int wake_aud_thread(struct client_stream *stream)
return 0;
}
+/* Stop the audio thread for the given stream.
+ * Args:
+ * stream - Stream for which to stop the audio thread.
+ * join - When non-zero, attempt to join the audio thread (wait for it to
+ * complete).
+ */
+static void stop_aud_thread(struct client_stream *stream, int join)
+{
+ if (thread_is_running(&stream->thread)) {
+ stream->thread.state = CRAS_THREAD_STOP;
+ wake_aud_thread(stream);
+ if (join)
+ pthread_join(stream->thread.tid, NULL);
+ }
+
+ if (stream->wake_fds[0] >= 0) {
+ close(stream->wake_fds[0]);
+ close(stream->wake_fds[1]);
+ stream->wake_fds[0] = -1;
+ }
+}
+
+/* Start the audio thread for this stream.
+ * Returns when the thread has started and is waiting.
+ * Args:
+ * stream - The stream that needs an audio thread.
+ * Returns:
+ * 0 for success, or a negative error code.
+ */
+static int start_aud_thread(struct client_stream *stream)
+{
+ int rc;
+ struct timespec future;
+
+ rc = pipe(stream->wake_fds);
+ if (rc < 0) {
+ rc = -errno;
+ syslog(LOG_ERR, "cras_client: pipe: %s", strerror(-rc));
+ return rc;
+ }
+
+ stream->thread.state = CRAS_THREAD_WARMUP;
+
+ pthread_mutex_lock(&stream->client->stream_start_lock);
+ rc = pthread_create(&stream->thread.tid, NULL, audio_thread, stream);
+ if (rc) {
+ pthread_mutex_unlock(&stream->client->stream_start_lock);
+ syslog(LOG_ERR,
+ "cras_client: Couldn't create audio stream: %s",
+ strerror(rc));
+ stream->thread.state = CRAS_THREAD_STOP;
+ stop_aud_thread(stream, 0);
+ return -rc;
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &future);
+ future.tv_sec += 2; /* Wait up to two seconds. */
+ rc = pthread_cond_timedwait(&stream->client->stream_start_cond,
+ &stream->client->stream_start_lock, &future);
+ pthread_mutex_unlock(&stream->client->stream_start_lock);
+ if (rc != 0) {
+ /* Something is very wrong: try to cancel the thread and don't
+ * wait for it. */
+ syslog(LOG_ERR, "cras_client: Client thread not responding: %s",
+ strerror(rc));
+ stop_aud_thread(stream, 0);
+ return -rc;
+ }
+ return 0;
+}
+
/*
* Client thread.
*/
@@ -630,20 +1360,14 @@ int end_server_state_read(const struct cras_server_state *state, unsigned count)
}
/* Gets the shared memory region used to share audio data with the server. */
-static int config_shm(struct cras_audio_shm *shm, int key, size_t size)
+static int config_shm(struct cras_audio_shm *shm, int shm_fd, size_t size)
{
- int shmid;
-
- shmid = shmget(key, size, 0600);
- if (shmid < 0) {
- syslog(LOG_ERR,
- "cras_client: shmget failed to get shm for stream.");
- return shmid;
- }
- shm->area = (struct cras_audio_shm_area *)shmat(shmid, NULL, 0);
+ shm->area = (struct cras_audio_shm_area *)mmap(
+ NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ shm_fd, 0);
if (shm->area == (struct cras_audio_shm_area *)-1) {
syslog(LOG_ERR,
- "cras_client: shmat failed to attach shm for stream.");
+ "cras_client: mmap failed to map shm for stream.");
return errno;
}
/* Copy server shm config locally. */
@@ -655,10 +1379,12 @@ static int config_shm(struct cras_audio_shm *shm, int key, size_t size)
/* Release shm areas if references to them are held. */
static void free_shm(struct client_stream *stream)
{
- if (stream->capture_shm.area)
- shmdt(stream->capture_shm.area);
- if (stream->play_shm.area)
- shmdt(stream->play_shm.area);
+ if (stream->capture_shm.area) {
+ munmap(stream->capture_shm.area, stream->capture_shm_size);
+ }
+ if (stream->play_shm.area) {
+ munmap(stream->play_shm.area, stream->play_shm_size);
+ }
stream->capture_shm.area = NULL;
stream->play_shm.area = NULL;
}
@@ -667,65 +1393,58 @@ static void free_shm(struct client_stream *stream)
* format converter, configure the shared memory region, and start the audio
* thread that will handle requests from the server. */
static int stream_connected(struct client_stream *stream,
- const struct cras_client_stream_connected *msg)
+ const struct cras_client_stream_connected *msg,
+ const int stream_fds[2], const unsigned int num_fds)
{
int rc;
struct cras_audio_format mfmt;
- if (msg->err) {
+ if (msg->err || num_fds != 2) {
syslog(LOG_ERR, "cras_client: Error Setting up stream %d\n",
msg->err);
- return msg->err;
+ rc = msg->err;
+ goto err_ret;
}
unpack_cras_audio_format(&mfmt, &msg->format);
if (cras_stream_has_input(stream->direction)) {
rc = config_shm(&stream->capture_shm,
- msg->input_shm_key,
+ stream_fds[0],
msg->shm_max_size);
if (rc < 0) {
syslog(LOG_ERR,
"cras_client: Error configuring capture shm");
goto err_ret;
}
+ stream->capture_shm_size = msg->shm_max_size;
}
if (cras_stream_uses_output_hw(stream->direction)) {
rc = config_shm(&stream->play_shm,
- msg->output_shm_key,
+ stream_fds[1],
msg->shm_max_size);
if (rc < 0) {
syslog(LOG_ERR,
"cras_client: Error configuring playback shm");
goto err_ret;
}
+ stream->play_shm_size = msg->shm_max_size;
cras_shm_set_volume_scaler(&stream->play_shm,
stream->volume_scaler);
}
- rc = pipe(stream->wake_fds);
- if (rc < 0) {
- goto err_ret;
- }
-
- stream->thread.running = 1;
-
- rc = pthread_create(&stream->thread.tid, NULL, audio_thread, stream);
- if (rc) {
- syslog(LOG_ERR,
- "cras_client: Couldn't create audio stream.");
- stream->thread.running = 0;
- goto err_ret;
- }
+ stream->thread.state = CRAS_THREAD_RUNNING;
+ wake_aud_thread(stream);
+ close(stream_fds[0]);
+ close(stream_fds[1]);
return 0;
err_ret:
- if (stream->wake_fds[0] >= 0) {
- close(stream->wake_fds[0]);
- close(stream->wake_fds[1]);
- }
+ stop_aud_thread(stream, 1);
+ close(stream_fds[0]);
+ close(stream_fds[1]);
free_shm(stream);
return rc;
}
@@ -741,7 +1460,8 @@ static int send_connect_message(struct cras_client *client,
/* Create a socket pair for the server to notify of audio events. */
rc = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
if (rc != 0) {
- syslog(LOG_ERR, "cras_client: socketpair fails.");
+ rc = -errno;
+ syslog(LOG_ERR, "cras_client: socketpair: %s", strerror(-rc));
goto fail;
}
@@ -754,8 +1474,8 @@ static int send_connect_message(struct cras_client *client,
stream->flags,
stream->config->format,
dev_idx);
- rc = cras_send_with_fd(client->server_fd, &serv_msg, sizeof(serv_msg),
- sock[1]);
+ rc = cras_send_with_fds(client->server_fd, &serv_msg, sizeof(serv_msg),
+ &sock[1], 1);
if (rc != sizeof(serv_msg)) {
rc = EIO;
syslog(LOG_ERR,
@@ -790,15 +1510,15 @@ static int client_thread_add_stream(struct cras_client *client,
/* Find the hotword device index. */
if ((stream->flags & HOTWORD_STREAM) == HOTWORD_STREAM &&
dev_idx == NO_DEVICE) {
- int aokr_idx;
- aokr_idx = cras_client_get_first_dev_type_idx(client,
- CRAS_NODE_TYPE_AOKR, CRAS_STREAM_INPUT);
- if (aokr_idx < 0) {
+ int hotword_idx;
+ hotword_idx = cras_client_get_first_dev_type_idx(client,
+ CRAS_NODE_TYPE_HOTWORD, CRAS_STREAM_INPUT);
+ if (hotword_idx < 0) {
syslog(LOG_ERR,
"cras_client: add_stream: Finding hotword dev");
- return aokr_idx;
+ return hotword_idx;
}
- dev_idx = aokr_idx;
+ dev_idx = hotword_idx;
}
/* Find an available stream id. */
@@ -812,10 +1532,18 @@ static int client_thread_add_stream(struct cras_client *client,
*stream_id_out = new_id;
stream->client = client;
+ /* Start the audio thread. */
+ rc = start_aud_thread(stream);
+ if (rc != 0)
+ return rc;
+
+ /* Start the thread associated with this stream. */
/* send a message to the server asking that the stream be started. */
rc = send_connect_message(client, stream, dev_idx);
- if (rc != 0)
+ if (rc != 0) {
+ stop_aud_thread(stream, 1);
return rc;
+ }
/* Add the stream to the linked list */
DL_APPEND(client->streams, stream);
@@ -837,19 +1565,16 @@ static int client_thread_rm_stream(struct cras_client *client,
return 0;
/* Tell server to remove. */
- cras_fill_disconnect_stream_message(&msg, stream_id);
- rc = write(client->server_fd, &msg, sizeof(msg));
- if (rc < 0)
- syslog(LOG_ERR,
- "cras_client: error removing stream from server\n");
-
- /* And shut down locally. */
- if (stream->thread.running) {
- stream->thread.running = 0;
- wake_aud_thread(stream);
- pthread_join(stream->thread.tid, NULL);
+ if (client->server_fd_state == CRAS_SOCKET_STATE_CONNECTED) {
+ cras_fill_disconnect_stream_message(&msg, stream_id);
+ rc = write(client->server_fd, &msg, sizeof(msg));
+ if (rc < 0)
+ syslog(LOG_ERR,
+ "cras_client: error removing stream from server\n");
}
+ /* And shut down locally. */
+ stop_aud_thread(stream, 1);
free_shm(stream);
@@ -857,10 +1582,6 @@ static int client_thread_rm_stream(struct cras_client *client,
if (stream->aud_fd >= 0)
close(stream->aud_fd);
- if (stream->wake_fds[0] >= 0) {
- close(stream->wake_fds[0]);
- close(stream->wake_fds[1]);
- }
free(stream->config);
free(stream);
@@ -886,38 +1607,52 @@ static int client_thread_set_stream_volume(struct cras_client *client,
}
/* Attach to the shm region containing the server state. */
-static int client_attach_shm(struct cras_client *client, key_t shm_key)
+static int client_attach_shm(struct cras_client *client, int shm_fd)
{
- int shmid;
+ int lock_rc;
+ int rc;
- /* Should only happen once per client lifetime. */
- if (client->server_state)
- return -EBUSY;
+ lock_rc = server_state_wrlock(client);
+ if (client->server_state) {
+ rc = -EBUSY;
+ goto error;
+ }
- shmid = shmget(shm_key, sizeof(*(client->server_state)), 0400);
- if (shmid < 0) {
+ client->server_state = (struct cras_server_state *)mmap(
+ NULL, sizeof(*client->server_state),
+ PROT_READ, MAP_SHARED, shm_fd, 0);
+ rc = -errno;
+ close(shm_fd);
+ if (client->server_state == (struct cras_server_state *)-1) {
syslog(LOG_ERR,
- "cras_client: shmget failed to get shm for client.");
- return shmid;
- }
- client->server_state = (struct cras_server_state *)
- shmat(shmid, NULL, SHM_RDONLY);
- if (client->server_state == (void *)-1) {
- client->server_state = NULL;
- syslog(LOG_ERR,
- "cras_client: shmat failed to attach shm for client.");
- return errno;
+ "cras_client: mmap failed to map shm for client: %s",
+ strerror(-rc));
+ goto error;
}
if (client->server_state->state_version != CRAS_SERVER_STATE_VERSION) {
- shmdt(client->server_state);
+ munmap((void *)client->server_state,
+ sizeof(*client->server_state));
client->server_state = NULL;
- syslog(LOG_ERR,
- "cras_client: Unknown server_state version.");
- return -EINVAL;
+ rc = -EINVAL;
+ syslog(LOG_ERR, "cras_client: Unknown server_state version.");
+ } else {
+ rc = 0;
}
- return 0;
+error:
+ server_state_unlock(client, lock_rc);
+ return rc;
+}
+
+static void cras_client_get_hotword_models_ready(
+ struct cras_client *client,
+ const char *hotword_models)
+{
+ if (!client->get_hotword_models_cb)
+ return;
+ client->get_hotword_models_cb(client, hotword_models);
+ client->get_hotword_models_cb = NULL;
}
/* Handles messages from the cras server. */
@@ -927,19 +1662,22 @@ static int handle_message_from_server(struct cras_client *client)
struct cras_client_message *msg;
int rc = 0;
int nread;
+ int server_fds[2];
+ unsigned int num_fds = 2;
msg = (struct cras_client_message *)buf;
- nread = recv(client->server_fd, buf, sizeof(buf), 0);
- if (nread < (int)sizeof(msg->length))
- goto read_error;
- if ((int)msg->length != nread)
- goto read_error;
+ nread = cras_recv_with_fds(client->server_fd, buf, sizeof(buf),
+ server_fds, &num_fds);
+ if (nread < (int)sizeof(msg->length) || (int)msg->length != nread)
+ return -EIO;
switch (msg->id) {
case CRAS_CLIENT_CONNECTED: {
struct cras_client_connected *cmsg =
(struct cras_client_connected *)msg;
- rc = client_attach_shm(client, cmsg->shm_key);
+ if (num_fds != 1)
+ return -EINVAL;
+ rc = client_attach_shm(client, server_fds[0]);
if (rc)
return rc;
client->id = cmsg->client_id;
@@ -953,7 +1691,7 @@ static int handle_message_from_server(struct cras_client *client)
stream_from_id(client, cmsg->stream_id);
if (stream == NULL)
break;
- rc = stream_connected(stream, cmsg);
+ rc = stream_connected(stream, cmsg, server_fds, num_fds);
if (rc < 0)
stream->config->err_cb(stream->client,
stream->id,
@@ -965,26 +1703,127 @@ static int handle_message_from_server(struct cras_client *client)
if (client->debug_info_callback)
client->debug_info_callback(client);
break;
+ case CRAS_CLIENT_GET_HOTWORD_MODELS_READY: {
+ struct cras_client_get_hotword_models_ready *cmsg =
+ (struct cras_client_get_hotword_models_ready *)msg;
+ cras_client_get_hotword_models_ready(client,
+ (const char *)cmsg->hotword_models);
+ break;
+ }
+ case CRAS_CLIENT_OUTPUT_VOLUME_CHANGED: {
+ struct cras_client_volume_changed *cmsg =
+ (struct cras_client_volume_changed *)msg;
+ if (client->observer_ops.output_volume_changed)
+ client->observer_ops.output_volume_changed(
+ client->observer_context,
+ cmsg->volume);
+ break;
+ }
+ case CRAS_CLIENT_OUTPUT_MUTE_CHANGED: {
+ struct cras_client_mute_changed *cmsg =
+ (struct cras_client_mute_changed *)msg;
+ if (client->observer_ops.output_mute_changed)
+ client->observer_ops.output_mute_changed(
+ client->observer_context,
+ cmsg->muted,
+ cmsg->user_muted,
+ cmsg->mute_locked);
+ break;
+ }
+ case CRAS_CLIENT_CAPTURE_GAIN_CHANGED: {
+ struct cras_client_volume_changed *cmsg =
+ (struct cras_client_volume_changed *)msg;
+ if (client->observer_ops.capture_gain_changed)
+ client->observer_ops.capture_gain_changed(
+ client->observer_context,
+ cmsg->volume);
+ break;
+ }
+ case CRAS_CLIENT_CAPTURE_MUTE_CHANGED: {
+ struct cras_client_mute_changed *cmsg =
+ (struct cras_client_mute_changed *)msg;
+ if (client->observer_ops.capture_mute_changed)
+ client->observer_ops.capture_mute_changed(
+ client->observer_context,
+ cmsg->muted,
+ cmsg->mute_locked);
+ break;
+ }
+ case CRAS_CLIENT_NODES_CHANGED: {
+ if (client->observer_ops.nodes_changed)
+ client->observer_ops.nodes_changed(
+ client->observer_context);
+ break;
+ }
+ case CRAS_CLIENT_ACTIVE_NODE_CHANGED: {
+ struct cras_client_active_node_changed *cmsg =
+ (struct cras_client_active_node_changed *)msg;
+ enum CRAS_STREAM_DIRECTION direction =
+ (enum CRAS_STREAM_DIRECTION)cmsg->direction;
+ if (client->observer_ops.active_node_changed)
+ client->observer_ops.active_node_changed(
+ client->observer_context,
+ direction, cmsg->node_id);
+ break;
+ }
+ case CRAS_CLIENT_OUTPUT_NODE_VOLUME_CHANGED: {
+ struct cras_client_node_value_changed *cmsg =
+ (struct cras_client_node_value_changed *)msg;
+ if (client->observer_ops.output_node_volume_changed)
+ client->observer_ops.output_node_volume_changed(
+ client->observer_context,
+ cmsg->node_id,
+ cmsg->value);
+ break;
+ }
+ case CRAS_CLIENT_NODE_LEFT_RIGHT_SWAPPED_CHANGED: {
+ struct cras_client_node_value_changed *cmsg =
+ (struct cras_client_node_value_changed *)msg;
+ if (client->observer_ops.node_left_right_swapped_changed)
+ client->observer_ops.node_left_right_swapped_changed(
+ client->observer_context,
+ cmsg->node_id,
+ cmsg->value);
+ break;
+ }
+ case CRAS_CLIENT_INPUT_NODE_GAIN_CHANGED: {
+ struct cras_client_node_value_changed *cmsg =
+ (struct cras_client_node_value_changed *)msg;
+ if (client->observer_ops.input_node_gain_changed)
+ client->observer_ops.input_node_gain_changed(
+ client->observer_context,
+ cmsg->node_id,
+ cmsg->value);
+ break;
+ }
+ case CRAS_CLIENT_NUM_ACTIVE_STREAMS_CHANGED: {
+ struct cras_client_num_active_streams_changed *cmsg =
+ (struct cras_client_num_active_streams_changed *)msg;
+ enum CRAS_STREAM_DIRECTION direction =
+ (enum CRAS_STREAM_DIRECTION)cmsg->direction;
+ if (client->observer_ops.num_active_streams_changed)
+ client->observer_ops.num_active_streams_changed(
+ client->observer_context,
+ direction, cmsg->num_active_streams);
+ break;
+ }
default:
break;
}
return 0;
-read_error:
- rc = connect_to_server_wait(client);
- if (rc < 0) {
- client->thread.running = 0;
- return -EIO;
- }
- return 0;
}
/* Handles messages from streams to this client. */
-static int handle_stream_message(struct cras_client *client)
+static int handle_stream_message(struct cras_client *client,
+ int poll_revents)
{
struct stream_msg msg;
int rc;
+ if ((poll_revents & POLLIN) == 0)
+ return 0;
+
rc = read(client->stream_fds[0], &msg, sizeof(msg));
if (rc < 0)
syslog(LOG_ERR, "cras_client: Stream read failed %d\n", errno);
@@ -996,12 +1835,16 @@ static int handle_stream_message(struct cras_client *client)
}
/* Handles messages from users to this client. */
-static int handle_command_message(struct cras_client *client)
+static int handle_command_message(struct cras_client *client,
+ int poll_revents)
{
uint8_t buf[MAX_CMD_MSG_LEN];
struct command_msg *msg = (struct command_msg *)buf;
int rc, to_read;
+ if ((poll_revents & POLLIN) == 0)
+ return 0;
+
rc = read(client->command_fds[0], buf, sizeof(msg->len));
if (rc != sizeof(msg->len) || msg->len > MAX_CMD_MSG_LEN) {
rc = -EIO;
@@ -1014,13 +1857,6 @@ static int handle_command_message(struct cras_client *client)
goto cmd_msg_complete;
}
- if (!check_server_connected_wait(client))
- if (connect_to_server_wait(client) < 0) {
- syslog(LOG_ERR, "cras_client: Lost server connection.");
- rc = -EIO;
- goto cmd_msg_complete;
- }
-
switch (msg->msg_id) {
case CLIENT_STOP: {
struct client_stream *s;
@@ -1030,7 +1866,7 @@ static int handle_command_message(struct cras_client *client)
client_thread_rm_stream(client, s->id);
/* And stop this client */
- client->thread.running = 0;
+ client->thread.state = CRAS_THREAD_STOP;
rc = 0;
break;
}
@@ -1055,7 +1891,10 @@ static int handle_command_message(struct cras_client *client)
break;
}
case CLIENT_SERVER_CONNECT:
- rc = connect_to_server_wait(client);
+ rc = connect_to_server_wait(client, false);
+ break;
+ case CLIENT_SERVER_CONNECT_ASYNC:
+ rc = server_connect(client);
break;
default:
assert(0);
@@ -1076,43 +1915,58 @@ cmd_msg_complete:
static void *client_thread(void *arg)
{
struct cras_client *client = (struct cras_client *)arg;
+ struct pollfd pollfds[4];
+ int (*cbs[4])(struct cras_client *client, int poll_revents);
+ unsigned int num_pollfds, i;
+ int rc;
if (arg == NULL)
return (void *)-EINVAL;
- while (client->thread.running) {
- struct pollfd pollfds[3];
- int (*cbs[3])(struct cras_client *client);
- unsigned int num_pollfds, i;
- int rc;
-
+ while (thread_is_running(&client->thread)) {
num_pollfds = 0;
- if (client->server_fd >= 0) {
- cbs[num_pollfds] = handle_message_from_server;
- pollfds[num_pollfds].fd = client->server_fd;
+
+ rc = cras_file_wait_get_fd(client->sock_file_wait);
+ if (rc >= 0) {
+ cbs[num_pollfds] = sock_file_wait_dispatch;
+ pollfds[num_pollfds].fd = rc;
pollfds[num_pollfds].events = POLLIN;
+ pollfds[num_pollfds].revents = 0;
+ num_pollfds++;
+ }
+ else
+ syslog(LOG_ERR, "file wait fd: %d", rc);
+ if (client->server_fd >= 0) {
+ cbs[num_pollfds] = server_fd_dispatch;
+ server_fill_pollfd(client, &(pollfds[num_pollfds]));
num_pollfds++;
}
if (client->command_fds[0] >= 0) {
cbs[num_pollfds] = handle_command_message;
pollfds[num_pollfds].fd = client->command_fds[0];
pollfds[num_pollfds].events = POLLIN;
+ pollfds[num_pollfds].revents = 0;
num_pollfds++;
}
if (client->stream_fds[0] >= 0) {
cbs[num_pollfds] = handle_stream_message;
pollfds[num_pollfds].fd = client->stream_fds[0];
pollfds[num_pollfds].events = POLLIN;
+ pollfds[num_pollfds].revents = 0;
num_pollfds++;
}
rc = poll(pollfds, num_pollfds, -1);
- if (rc < 0)
+ if (rc <= 0)
continue;
for (i = 0; i < num_pollfds; i++) {
- if (pollfds[i].revents & POLLIN)
- cbs[i](client);
+ /* Only do one at a time, since some messages may
+ * result in change to other fds. */
+ if (pollfds[i].revents) {
+ cbs[i](client, pollfds[i].revents);
+ break;
+ }
}
}
@@ -1129,7 +1983,7 @@ static int send_command_message(struct cras_client *client,
struct command_msg *msg)
{
int rc, cmd_res;
- if (client == NULL || !client->thread.running)
+ if (client == NULL || !thread_is_running(&client->thread))
return -EINVAL;
rc = write(client->command_fds[1], msg, msg->len);
@@ -1176,23 +2030,25 @@ static int send_stream_volume_command_msg(struct cras_client *client,
static int write_message_to_server(struct cras_client *client,
const struct cras_server_message *msg)
{
- if (write(client->server_fd, msg, msg->length) !=
- (ssize_t)msg->length) {
- int rc = 0;
+ ssize_t write_rc = -EPIPE;
- /* Write to server failed, try to re-connect. */
- if (client->thread.running)
- rc = send_simple_cmd_msg(client, 0,
- CLIENT_SERVER_CONNECT);
- else
- rc = connect_to_server_wait(client);
- if (rc < 0)
- return rc;
- if (write(client->server_fd, msg, msg->length) !=
- (ssize_t)msg->length)
- return -EINVAL;
+ if (client->server_fd_state == CRAS_SOCKET_STATE_CONNECTED ||
+ client->server_fd_state == CRAS_SOCKET_STATE_FIRST_MESSAGE) {
+ write_rc = write(client->server_fd, msg, msg->length);
+ if (write_rc < 0)
+ write_rc = -errno;
}
- return 0;
+
+ if (write_rc != (ssize_t)msg->length &&
+ client->server_fd_state != CRAS_SOCKET_STATE_FIRST_MESSAGE)
+ return -EPIPE;
+
+ if (write_rc < 0)
+ return write_rc;
+ else if (write_rc != (ssize_t)msg->length)
+ return -EIO;
+ else
+ return 0;
}
/*
@@ -1201,14 +2057,77 @@ static int write_message_to_server(struct cras_client *client,
int cras_client_create(struct cras_client **client)
{
+ const char *sock_dir;
+ size_t sock_file_size;
int rc;
+ struct client_int *client_int;
+ pthread_condattr_t cond_attr;
+
+ /* Ignore SIGPIPE while using this API. */
+ signal(SIGPIPE, SIG_IGN);
+
+ sock_dir = cras_config_get_system_socket_file_dir();
+ if (!sock_dir)
+ return -ENOMEM;
- *client = (struct cras_client *)calloc(1, sizeof(struct cras_client));
- if (*client == NULL)
+ client_int = (struct client_int *)calloc(1, sizeof(*client_int));
+ if (!client_int)
return -ENOMEM;
+ *client = &client_int->client;
(*client)->server_fd = -1;
(*client)->id = -1;
+ rc = pthread_rwlock_init(&client_int->server_state_rwlock, NULL);
+ if (rc != 0) {
+ syslog(LOG_ERR, "cras_client: Could not init state rwlock.");
+ rc = -rc;
+ goto free_client;
+ }
+
+ rc = pthread_mutex_init(&(*client)->stream_start_lock, NULL);
+ if (rc != 0) {
+ syslog(LOG_ERR, "cras_client: Could not init start lock.");
+ rc = -rc;
+ goto free_rwlock;
+ }
+
+ pthread_condattr_init(&cond_attr);
+ pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
+ rc = pthread_cond_init(&(*client)->stream_start_cond, &cond_attr);
+ pthread_condattr_destroy(&cond_attr);
+ if (rc != 0) {
+ syslog(LOG_ERR, "cras_client: Could not init start cond.");
+ rc = -rc;
+ goto free_lock;
+ }
+
+ (*client)->server_event_fd = eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK);
+ if ((*client)->server_event_fd < 0) {
+ syslog(LOG_ERR, "cras_client: Could not setup server eventfd.");
+ rc = -errno;
+ goto free_cond;
+ }
+
+ sock_file_size = strlen(sock_dir) + strlen(CRAS_SOCKET_FILE) + 2;
+ (*client)->sock_file = (const char *)malloc(sock_file_size);
+ if (!(*client)->sock_file) {
+ rc = -ENOMEM;
+ goto free_error;
+ }
+ snprintf((char *)(*client)->sock_file, sock_file_size, "%s/%s", sock_dir,
+ CRAS_SOCKET_FILE);
+
+ rc = cras_file_wait_create((*client)->sock_file,
+ CRAS_FILE_WAIT_FLAG_NONE,
+ sock_file_wait_callback, *client,
+ &(*client)->sock_file_wait);
+ if (rc != 0 && rc != -ENOENT) {
+ syslog(LOG_ERR, "cras_client: Could not setup watch for '%s'.",
+ (*client)->sock_file);
+ goto free_error;
+ }
+ (*client)->sock_file_exists = (rc == 0);
+
/* Pipes used by the main thread and the client thread to send commands
* and replies. */
rc = pipe((*client)->command_fds);
@@ -1227,53 +2146,51 @@ int cras_client_create(struct cras_client **client)
return 0;
free_error:
- free(*client);
+ 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_cond:
+ pthread_cond_destroy(&(*client)->stream_start_cond);
+free_lock:
+ pthread_mutex_destroy(&(*client)->stream_start_lock);
+free_rwlock:
+ pthread_rwlock_destroy(&client_int->server_state_rwlock);
+free_client:
*client = NULL;
+ free(client_int);
return rc;
}
-static inline
-int shutdown_and_close_socket(int sockfd)
-{
- int rc;
- uint8_t buffer[CRAS_CLIENT_MAX_MSG_SIZE];
- struct timeval tv;
-
- tv.tv_sec = 0;
- tv.tv_usec = SERVER_SHUTDOWN_TIMEOUT_US;
- setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
-
- rc = shutdown(sockfd, SHUT_WR);
- if (rc < 0)
- return rc;
- /* Wait until the socket is closed by the peer. */
- for (;;) {
- rc = recv(sockfd, buffer, sizeof(buffer), 0);
- if (rc <= 0)
- break;
- }
- return close(sockfd);
-}
-
void cras_client_destroy(struct cras_client *client)
{
+ struct client_int *client_int;
if (client == NULL)
return;
+ client_int = to_client_int(client);
+ client->server_connection_cb = NULL;
+ client->server_err_cb = NULL;
cras_client_stop(client);
- if (client->server_state)
- shmdt(client->server_state);
- if (client->server_fd >= 0)
- shutdown_and_close_socket(client->server_fd);
+ server_disconnect(client);
close(client->command_fds[0]);
close(client->command_fds[1]);
close(client->stream_fds[0]);
close(client->stream_fds[1]);
- free(client);
+ cras_file_wait_destroy(client->sock_file_wait);
+ pthread_rwlock_destroy(&client_int->server_state_rwlock);
+ free((void *)client->sock_file);
+ free(client_int);
}
int cras_client_connect(struct cras_client *client)
{
- return connect_to_server(client);
+ return connect_to_server(client, NULL, true);
+}
+
+int cras_client_connect_timeout(struct cras_client *client,
+ unsigned int timeout_ms)
+{
+ return connect_to_server_wait_retry(client, timeout_ms, true);
}
int cras_client_connected_wait(struct cras_client *client)
@@ -1281,6 +2198,11 @@ int cras_client_connected_wait(struct cras_client *client)
return send_simple_cmd_msg(client, 0, CLIENT_SERVER_CONNECT);
}
+int cras_client_connect_async(struct cras_client *client)
+{
+ return send_simple_cmd_msg(client, 0, CLIENT_SERVER_CONNECT_ASYNC);
+}
+
struct cras_stream_params *cras_client_stream_params_create(
enum CRAS_STREAM_DIRECTION direction,
size_t buffer_frames,
@@ -1530,84 +2452,155 @@ int cras_client_set_system_capture_mute_locked(struct cras_client *client,
return write_message_to_server(client, &msg.header);
}
-size_t cras_client_get_system_volume(struct cras_client *client)
+size_t cras_client_get_system_volume(const struct cras_client *client)
{
- if (!client || !client->server_state)
+ size_t volume;
+ int lock_rc;
+
+ lock_rc = server_state_rdlock(client);
+ if (lock_rc)
return 0;
- return client->server_state->volume;
+
+ volume = client->server_state->volume;
+ server_state_unlock(client, lock_rc);
+ return volume;
}
-long cras_client_get_system_capture_gain(struct cras_client *client)
+long cras_client_get_system_capture_gain(const struct cras_client *client)
{
- if (!client || !client->server_state)
+ long gain;
+ int lock_rc;
+
+ lock_rc = server_state_rdlock(client);
+ if (lock_rc)
return 0;
- return client->server_state->capture_gain;
+
+ gain = client->server_state->capture_gain;
+ server_state_unlock(client, lock_rc);
+ return gain;
}
-int cras_client_get_system_muted(struct cras_client *client)
+int cras_client_get_system_muted(const struct cras_client *client)
{
- if (!client || !client->server_state)
+ int muted;
+ int lock_rc;
+
+ lock_rc = server_state_rdlock(client);
+ if (lock_rc)
return 0;
- return client->server_state->mute;
+
+ muted = client->server_state->mute;
+ server_state_unlock(client, lock_rc);
+ return muted;
}
-int cras_client_get_user_muted(struct cras_client *client)
+int cras_client_get_user_muted(const struct cras_client *client)
{
- if (!client || !client->server_state)
+ int muted;
+ int lock_rc;
+
+ lock_rc = server_state_rdlock(client);
+ if (lock_rc)
return 0;
- return client->server_state->user_mute;
+
+ muted = client->server_state->user_mute;
+ server_state_unlock(client, lock_rc);
+ return muted;
}
-int cras_client_get_system_capture_muted(struct cras_client *client)
+int cras_client_get_system_capture_muted(const struct cras_client *client)
{
- if (!client || !client->server_state)
+ int muted;
+ int lock_rc;
+
+ lock_rc = server_state_rdlock(client);
+ if (lock_rc)
return 0;
- return client->server_state->capture_mute;
+
+ muted = client->server_state->capture_mute;
+ server_state_unlock(client, lock_rc);
+ return muted;
}
-long cras_client_get_system_min_volume(struct cras_client *client)
+long cras_client_get_system_min_volume(const struct cras_client *client)
{
- if (!client || !client->server_state)
+ long min_volume;
+ int lock_rc;
+
+ lock_rc = server_state_rdlock(client);
+ if (lock_rc)
return 0;
- return client->server_state->min_volume_dBFS;
+
+ min_volume = client->server_state->min_volume_dBFS;
+ server_state_unlock(client, lock_rc);
+ return min_volume;
}
-long cras_client_get_system_max_volume(struct cras_client *client)
+long cras_client_get_system_max_volume(const struct cras_client *client)
{
- if (!client || !client->server_state)
+ long max_volume;
+ int lock_rc;
+
+ lock_rc = server_state_rdlock(client);
+ if (lock_rc)
return 0;
- return client->server_state->max_volume_dBFS;
+
+ max_volume = client->server_state->max_volume_dBFS;
+ server_state_unlock(client, lock_rc);
+ return max_volume;
}
-long cras_client_get_system_min_capture_gain(struct cras_client *client)
+long cras_client_get_system_min_capture_gain(const struct cras_client *client)
{
- if (!client || !client->server_state)
+ long min_gain;
+ int lock_rc;
+
+ lock_rc = server_state_rdlock(client);
+ if (lock_rc)
return 0;
- return client->server_state->min_capture_gain;
+
+ min_gain = client->server_state->min_capture_gain;
+ server_state_unlock(client, lock_rc);
+ return min_gain;
}
-long cras_client_get_system_max_capture_gain(struct cras_client *client)
+long cras_client_get_system_max_capture_gain(const struct cras_client *client)
{
- if (!client || !client->server_state)
+ long max_gain;
+ int lock_rc;
+
+ lock_rc = server_state_rdlock(client);
+ if (lock_rc)
return 0;
- return client->server_state->max_capture_gain;
+
+ max_gain = client->server_state->max_capture_gain;
+ server_state_unlock(client, lock_rc);
+ return max_gain;
}
const struct audio_debug_info *cras_client_get_audio_debug_info(
- struct cras_client *client)
+ const struct cras_client *client)
{
- if (!client || !client->server_state)
- return NULL;
+ const struct audio_debug_info *debug_info;
+ int lock_rc;
+
+ lock_rc = server_state_rdlock(client);
+ if (lock_rc)
+ return 0;
- return &client->server_state->audio_debug_info;
+ debug_info = &client->server_state->audio_debug_info;
+ server_state_unlock(client, lock_rc);
+ return debug_info;
}
-unsigned cras_client_get_num_active_streams(struct cras_client *client,
+unsigned cras_client_get_num_active_streams(const struct cras_client *client,
struct timespec *ts)
{
unsigned num_streams, version, i;
+ int lock_rc;
- if (!client || !client->server_state)
+ lock_rc = server_state_rdlock(client);
+ if (lock_rc)
return 0;
read_active_streams_again:
@@ -1625,30 +2618,40 @@ read_active_streams_again:
if (end_server_state_read(client->server_state, version))
goto read_active_streams_again;
+ server_state_unlock(client, lock_rc);
return num_streams;
}
int cras_client_run_thread(struct cras_client *client)
{
- if (client == NULL || client->thread.running)
+ int rc;
+
+ if (client == NULL)
return -EINVAL;
+ if (thread_is_running(&client->thread))
+ return 0;
assert(client->command_reply_fds[0] == -1 &&
client->command_reply_fds[1] == -1);
- client->thread.running = 1;
if (pipe(client->command_reply_fds) < 0)
return -EIO;
- if (pthread_create(&client->thread.tid, NULL, client_thread, client))
- return -ENOMEM;
+ client->thread.state = CRAS_THREAD_RUNNING;
+ rc = pthread_create(&client->thread.tid, NULL, client_thread, client);
+ if (rc) {
+ client->thread.state = CRAS_THREAD_STOP;
+ return -rc;
+ }
return 0;
}
int cras_client_stop(struct cras_client *client)
{
- if (client == NULL || !client->thread.running)
+ if (client == NULL)
return -EINVAL;
+ if (!thread_is_running(&client->thread))
+ return 0;
send_simple_cmd_msg(client, 0, CLIENT_STOP);
pthread_join(client->thread.tid, NULL);
@@ -1661,6 +2664,29 @@ int cras_client_stop(struct cras_client *client)
return 0;
}
+void cras_client_set_server_error_cb(struct cras_client *client,
+ cras_server_error_cb_t err_cb,
+ void *user_arg)
+{
+ client->server_err_cb = err_cb;
+ client->server_connection_user_arg = user_arg;
+}
+
+void cras_client_set_connection_status_cb(
+ struct cras_client *client,
+ cras_connection_status_cb_t connection_cb,
+ void *user_arg)
+{
+ client->server_connection_cb = connection_cb;
+ client->server_connection_user_arg = user_arg;
+}
+
+void cras_client_set_thread_priority_cb(struct cras_client *client,
+ cras_thread_priority_cb_t cb)
+{
+ client->thread_priority_cb = cb;
+}
+
int cras_client_get_output_devices(const struct cras_client *client,
struct cras_iodev_info *devs,
struct cras_ionode_info *nodes,
@@ -1668,12 +2694,12 @@ int cras_client_get_output_devices(const struct cras_client *client,
{
const struct cras_server_state *state;
unsigned avail_devs, avail_nodes, version;
+ int lock_rc;
- if (!client)
+ lock_rc = server_state_rdlock(client);
+ if (lock_rc)
return -EINVAL;
state = client->server_state;
- if (!state)
- return -EINVAL;
read_outputs_again:
version = begin_server_state_read(state);
@@ -1683,6 +2709,7 @@ read_outputs_again:
memcpy(nodes, state->output_nodes, avail_nodes * sizeof(*nodes));
if (end_server_state_read(state, version))
goto read_outputs_again;
+ server_state_unlock(client, lock_rc);
*num_devs = avail_devs;
*num_nodes = avail_nodes;
@@ -1697,12 +2724,12 @@ int cras_client_get_input_devices(const struct cras_client *client,
{
const struct cras_server_state *state;
unsigned avail_devs, avail_nodes, version;
+ int lock_rc;
+ lock_rc = server_state_rdlock(client);
if (!client)
return -EINVAL;
state = client->server_state;
- if (!state)
- return -EINVAL;
read_inputs_again:
version = begin_server_state_read(state);
@@ -1712,6 +2739,7 @@ read_inputs_again:
memcpy(nodes, state->input_nodes, avail_nodes * sizeof(*nodes));
if (end_server_state_read(state, version))
goto read_inputs_again;
+ server_state_unlock(client, lock_rc);
*num_devs = avail_devs;
*num_nodes = avail_nodes;
@@ -1725,12 +2753,12 @@ int cras_client_get_attached_clients(const struct cras_client *client,
{
const struct cras_server_state *state;
unsigned num, version;
+ int lock_rc;
- if (!client)
+ lock_rc = server_state_rdlock(client);
+ if (lock_rc)
return -EINVAL;
state = client->server_state;
- if (!state)
- return 0;
read_clients_again:
version = begin_server_state_read(state);
@@ -1738,6 +2766,7 @@ read_clients_again:
memcpy(clients, state->client_info, num * sizeof(*clients));
if (end_server_state_read(state, version))
goto read_clients_again;
+ server_state_unlock(client, lock_rc);
return num;
}
@@ -2072,6 +3101,23 @@ int cras_client_test_iodev_command(struct cras_client *client,
return rc;
}
+int cras_client_config_global_remix(struct cras_client *client,
+ unsigned num_channels,
+ float *coefficient)
+{
+ struct cras_config_global_remix *msg;
+ int rc;
+
+ msg = (struct cras_config_global_remix *)malloc(sizeof(*msg) +
+ num_channels * num_channels * sizeof(*coefficient));
+ cras_fill_config_global_remix_command(msg, num_channels,
+ coefficient,
+ num_channels * num_channels);
+ rc = write_message_to_server(client, &msg->header);
+ free(msg);
+ return rc;
+}
+
/* Return the index of the device used for listening to hotwords. */
int cras_client_get_first_dev_type_idx(const struct cras_client *client,
enum CRAS_NODE_TYPE type,
@@ -2082,12 +3128,12 @@ int cras_client_get_first_dev_type_idx(const struct cras_client *client,
unsigned int i;
const struct cras_ionode_info *node_list;
unsigned int num_nodes;
+ int lock_rc;
- if (!client)
+ lock_rc = server_state_rdlock(client);
+ if (lock_rc)
return -EINVAL;
state = client->server_state;
- if (!state)
- return -EINVAL;
read_nodes_again:
version = begin_server_state_read(state);
@@ -2099,11 +3145,15 @@ read_nodes_again:
num_nodes = state->num_input_nodes;
}
for (i = 0; i < num_nodes; i++) {
- if ((enum CRAS_NODE_TYPE)node_list[i].type_enum == type)
- return node_list[i].iodev_idx;
+ if ((enum CRAS_NODE_TYPE)node_list[i].type_enum == type) {
+ int ret_idx = node_list[i].iodev_idx;
+ server_state_unlock(client, lock_rc);
+ return ret_idx;
+ }
}
if (end_server_state_read(state, version))
goto read_nodes_again;
+ server_state_unlock(client, lock_rc);
return -ENODEV;
}
@@ -2115,3 +3165,238 @@ int cras_client_set_suspend(struct cras_client *client, int suspend)
cras_fill_suspend_message(&msg, suspend);
return write_message_to_server(client, &msg);
}
+
+int cras_client_get_hotword_models(struct cras_client *client,
+ cras_node_id_t node_id,
+ get_hotword_models_cb_t cb)
+{
+ struct cras_get_hotword_models msg;
+
+ if (!client)
+ return -EINVAL;
+ client->get_hotword_models_cb = cb;
+
+ cras_fill_get_hotword_models_message(&msg, node_id);
+ return write_message_to_server(client, &msg.header);
+}
+
+int cras_client_set_hotword_model(struct cras_client *client,
+ cras_node_id_t node_id,
+ const char *model_name)
+{
+ struct cras_set_hotword_model msg;
+
+ cras_fill_set_hotword_model_message(&msg, node_id, model_name);
+ return write_message_to_server(client, &msg.header);
+}
+
+void cras_client_set_state_change_callback_context(
+ struct cras_client *client, void *context)
+{
+ if (!client)
+ return;
+ client->observer_context = context;
+}
+
+static int cras_send_register_notification(struct cras_client *client,
+ enum CRAS_CLIENT_MESSAGE_ID msg_id,
+ int do_register)
+{
+ struct cras_register_notification msg;
+ int rc;
+
+ /* This library automatically re-registers notifications when
+ * reconnecting, so we can ignore message send failure due to no
+ * connection. */
+ cras_fill_register_notification_message(&msg, msg_id, do_register);
+ rc = write_message_to_server(client, &msg.header);
+ if (rc == -EPIPE)
+ rc = 0;
+ return rc;
+}
+
+int cras_client_set_output_volume_changed_callback(
+ struct cras_client *client,
+ cras_client_output_volume_changed_callback cb)
+{
+ if (!client)
+ return -EINVAL;
+ client->observer_ops.output_volume_changed = cb;
+ return cras_send_register_notification(
+ client, CRAS_CLIENT_OUTPUT_VOLUME_CHANGED, cb != NULL);
+}
+
+int cras_client_set_output_mute_changed_callback(
+ struct cras_client *client,
+ cras_client_output_mute_changed_callback cb)
+{
+ if (!client)
+ return -EINVAL;
+ client->observer_ops.output_mute_changed = cb;
+ return cras_send_register_notification(
+ client, CRAS_CLIENT_OUTPUT_MUTE_CHANGED, cb != NULL);
+}
+
+int cras_client_set_capture_gain_changed_callback(
+ struct cras_client *client,
+ cras_client_capture_gain_changed_callback cb)
+{
+ if (!client)
+ return -EINVAL;
+ client->observer_ops.capture_gain_changed = cb;
+ return cras_send_register_notification(
+ client, CRAS_CLIENT_CAPTURE_GAIN_CHANGED, cb != NULL);
+}
+
+int cras_client_set_capture_mute_changed_callback(
+ struct cras_client *client,
+ cras_client_capture_mute_changed_callback cb)
+{
+ if (!client)
+ return -EINVAL;
+ client->observer_ops.capture_mute_changed = cb;
+ return cras_send_register_notification(
+ client, CRAS_CLIENT_CAPTURE_MUTE_CHANGED, cb != NULL);
+}
+
+int cras_client_set_nodes_changed_callback(
+ struct cras_client *client,
+ cras_client_nodes_changed_callback cb)
+{
+ if (!client)
+ return -EINVAL;
+ client->observer_ops.nodes_changed = cb;
+ return cras_send_register_notification(
+ client, CRAS_CLIENT_NODES_CHANGED, cb != NULL);
+}
+
+int cras_client_set_active_node_changed_callback(
+ struct cras_client *client,
+ cras_client_active_node_changed_callback cb)
+{
+ if (!client)
+ return -EINVAL;
+ client->observer_ops.active_node_changed = cb;
+ return cras_send_register_notification(
+ client, CRAS_CLIENT_ACTIVE_NODE_CHANGED, cb != NULL);
+}
+
+int cras_client_set_output_node_volume_changed_callback(
+ struct cras_client *client,
+ cras_client_output_node_volume_changed_callback cb)
+{
+ if (!client)
+ return -EINVAL;
+ client->observer_ops.output_node_volume_changed = cb;
+ return cras_send_register_notification(
+ client, CRAS_CLIENT_OUTPUT_NODE_VOLUME_CHANGED, cb != NULL);
+}
+
+int cras_client_set_node_left_right_swapped_changed_callback(
+ struct cras_client *client,
+ cras_client_node_left_right_swapped_changed_callback cb)
+{
+ if (!client)
+ return -EINVAL;
+ client->observer_ops.node_left_right_swapped_changed = cb;
+ return cras_send_register_notification(
+ client, CRAS_CLIENT_NODE_LEFT_RIGHT_SWAPPED_CHANGED, cb != NULL);
+}
+
+int cras_client_set_input_node_gain_changed_callback(
+ struct cras_client *client,
+ cras_client_input_node_gain_changed_callback cb)
+{
+ if (!client)
+ return -EINVAL;
+ client->observer_ops.input_node_gain_changed = cb;
+ return cras_send_register_notification(
+ client, CRAS_CLIENT_INPUT_NODE_GAIN_CHANGED, cb != NULL);
+}
+
+int cras_client_set_num_active_streams_changed_callback(
+ struct cras_client *client,
+ cras_client_num_active_streams_changed_callback cb)
+{
+ if (!client)
+ return -EINVAL;
+ client->observer_ops.num_active_streams_changed = cb;
+ return cras_send_register_notification(
+ client, CRAS_CLIENT_NUM_ACTIVE_STREAMS_CHANGED, cb != NULL);
+}
+
+static int reregister_notifications(struct cras_client *client)
+{
+ int rc;
+
+ if (client->observer_ops.output_volume_changed) {
+ rc = cras_client_set_output_volume_changed_callback(
+ client,
+ client->observer_ops.output_volume_changed);
+ if (rc != 0)
+ return rc;
+ }
+ if (client->observer_ops.output_mute_changed) {
+ rc = cras_client_set_output_mute_changed_callback(
+ client,
+ client->observer_ops.output_mute_changed);
+ if (rc != 0)
+ return rc;
+ }
+ if (client->observer_ops.capture_gain_changed) {
+ rc = cras_client_set_capture_gain_changed_callback(
+ client,
+ client->observer_ops.capture_gain_changed);
+ if (rc != 0)
+ return rc;
+ }
+ if (client->observer_ops.capture_mute_changed) {
+ rc = cras_client_set_capture_mute_changed_callback(
+ client,
+ client->observer_ops.capture_mute_changed);
+ if (rc != 0)
+ return rc;
+ }
+ if (client->observer_ops.nodes_changed) {
+ rc = cras_client_set_nodes_changed_callback(
+ client, client->observer_ops.nodes_changed);
+ if (rc != 0)
+ return rc;
+ }
+ if (client->observer_ops.active_node_changed) {
+ rc = cras_client_set_active_node_changed_callback(
+ client,
+ client->observer_ops.active_node_changed);
+ if (rc != 0)
+ return rc;
+ }
+ if (client->observer_ops.output_node_volume_changed) {
+ rc = cras_client_set_output_node_volume_changed_callback(
+ client,
+ client->observer_ops.output_node_volume_changed);
+ if (rc != 0)
+ return rc;
+ }
+ if (client->observer_ops.node_left_right_swapped_changed) {
+ rc = cras_client_set_node_left_right_swapped_changed_callback(
+ client,
+ client->observer_ops.node_left_right_swapped_changed);
+ if (rc != 0)
+ return rc;
+ }
+ if (client->observer_ops.input_node_gain_changed) {
+ rc = cras_client_set_input_node_gain_changed_callback(
+ client,
+ client->observer_ops.input_node_gain_changed);
+ if (rc != 0)
+ return rc;
+ }
+ if (client->observer_ops.num_active_streams_changed) {
+ rc = cras_client_set_num_active_streams_changed_callback(
+ client,
+ client->observer_ops.num_active_streams_changed);
+ if (rc != 0)
+ return rc;
+ }
+ return 0;
+}
diff --git a/cras/src/libcras/cras_client.h b/cras/src/libcras/cras_client.h
index 614dec48..d17b6503 100644
--- a/cras/src/libcras/cras_client.h
+++ b/cras/src/libcras/cras_client.h
@@ -1,6 +1,46 @@
/* Copyright (c) 2012 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.
+ *
+ * This API creates multiple threads, one for control, and a thread per audio
+ * stream. The control thread is used to receive messages and notifications
+ * from the audio server, and manage the per-stream threads. API calls below
+ * may send messages to the control thread, or directly to the server. It is
+ * required that the control thread is running in order to support audio
+ * streams and notifications from the server.
+ *
+ * The API has multiple initialization sequences, but some of those can block
+ * while waiting for a response from the server.
+ *
+ * The following is the non-blocking API initialization sequence:
+ * cras_client_create()
+ * cras_client_set_connection_status_cb() (optional)
+ * cras_client_run_thread()
+ * cras_client_connect_async()
+ *
+ * The connection callback is executed asynchronously from the control thread
+ * when the connection has been established. The connection callback should be
+ * used to turn on or off interactions with any API call that communicates with
+ * the audio server or starts/stops audio streams. The above is implemented by
+ * cras_helper_create_connect_async().
+ *
+ * The following alternative (deprecated) initialization sequence can ensure
+ * that the connection is established synchronously.
+ *
+ * Just connect to the server (no control thread):
+ * cras_client_create()
+ * cras_client_set_server_connection_cb() (optional)
+ * one of:
+ * cras_client_connect() (blocks forever)
+ * or
+ * cras_client_connect_timeout() (blocks for timeout)
+ *
+ * For API calls below that require the control thread to be running:
+ * cras_client_run_thread();
+ * cras_client_connected_wait(); (blocks up to 1 sec.)
+ *
+ * The above minus setting the connection callback is implemented within
+ * cras_helper_create_connect().
*/
#ifndef CRAS_CLIENT_H_
@@ -10,6 +50,7 @@
extern "C" {
#endif
+#include <stdbool.h>
#include <stdint.h>
#include <sys/select.h>
@@ -29,7 +70,9 @@ struct cras_stream_params;
* sample_time - Playback time for the first sample read/written.
* user_arg - Value passed to add_stream;
* Return:
- * 0 on success, or a negative number if there is a stream-fatal error.
+ * Returns the number of frames read or written on success, or a negative
+ * number if there is a stream-fatal error. Returns EOF when the end of the
+ * stream is reached.
*/
typedef int (*cras_playback_cb_t)(struct cras_client *client,
cras_stream_id_t stream_id,
@@ -49,7 +92,9 @@ typedef int (*cras_playback_cb_t)(struct cras_client *client,
* playback_time - Playback time for the first sample written.
* user_arg - Value passed to add_stream;
* Return:
- * 0 on success, or a negative number if there is a stream-fatal error.
+ * Returns the number of frames read or written on success, or a negative
+ * number if there is a stream-fatal error. Returns EOF when the end of the
+ * stream is reached.
*/
typedef int (*cras_unified_cb_t)(struct cras_client *client,
cras_stream_id_t stream_id,
@@ -60,12 +105,78 @@ typedef int (*cras_unified_cb_t)(struct cras_client *client,
const struct timespec *playback_time,
void *user_arg);
-/* Callback for handling errors. */
+/* Callback for handling stream errors.
+ * Args:
+ * client - The client created with cras_client_create().
+ * stream_id - The ID for this stream.
+ * error - The error code,
+ * user_arg - The argument defined in cras_client_*_params_create().
+ */
typedef int (*cras_error_cb_t)(struct cras_client *client,
cras_stream_id_t stream_id,
int error,
void *user_arg);
+/* Callback for handling server error. DEPRECATED
+ *
+ * Deprecated by cras_server_connection_status_cb_t: use that instead.
+ * This is equivalent to CRAS_CONN_STATUS_FAILED.
+ *
+ * This callback is executed rarely: only when the connection to the server has
+ * already been interrupted and could not be re-established due to resource
+ * allocation failure (memory or file-descriptors). The caller may attempt
+ * to reestablish communication once those resources are available with
+ * cras_client_connect_async(), or (blocking) cras_client_connect().
+ *
+ * Args:
+ * client - The client created with cras_client_create().
+ * user_arg - The argument defined in cras_client_set_server_errro_cb().
+ */
+typedef void (*cras_server_error_cb_t)(struct cras_client *client,
+ void *user_arg);
+
+/* Server connection status. */
+typedef enum cras_connection_status {
+ CRAS_CONN_STATUS_FAILED,
+ /* Resource allocation problem. Free resources, and retry the
+ * connection with cras_client_connect_async(), or (blocking)
+ * cras_client_connect(). Do not call cras_client_connect(),
+ * cras_client_connect_timeout(), or cras_client_destroy()
+ * from the callback. */
+ CRAS_CONN_STATUS_DISCONNECTED,
+ /* The control thread is attempting to reconnect to the
+ * server in the background. Any attempt to access the
+ * server will fail or block (see
+ * cras_client_set_server_message_blocking(). */
+ CRAS_CONN_STATUS_CONNECTED,
+ /* Connection is established. All state change callbacks
+ * have been re-registered, but audio streams must be
+ * restarted, and node state data must be updated. */
+} cras_connection_status_t;
+
+/* Callback for handling server connection status.
+ *
+ * See also cras_client_set_connection_status_cb(). Do not call
+ * cras_client_connect(), cras_client_connect_timeout(), or
+ * cras_client_destroy() from this callback.
+ *
+ * Args:
+ * client - The client created with cras_client_create().
+ * status - The status of the connection to the server.
+ * user_arg - The argument defined in
+ * cras_client_set_connection_status_cb().
+ */
+typedef void (*cras_connection_status_cb_t)(struct cras_client *client,
+ cras_connection_status_t status,
+ void *user_arg);
+
+/* Callback for setting thread priority. */
+typedef void (*cras_thread_priority_cb_t)(struct cras_client *client);
+
+/* Callback for handling get hotword models reply. */
+typedef void (*get_hotword_models_cb_t)(struct cras_client *client,
+ const char *hotword_models);
+
/*
* Client handling.
*/
@@ -86,6 +197,7 @@ int cras_client_create(struct cras_client **client);
void cras_client_destroy(struct cras_client *client);
/* Connects a client to the running server.
+ * Waits forever (until interrupted or connected).
* Args:
* client - pointer returned from "cras_client_create".
* Returns:
@@ -93,33 +205,112 @@ void cras_client_destroy(struct cras_client *client);
*/
int cras_client_connect(struct cras_client *client);
-/* Waits for the server to indicate that the client is connected. Useful to
- * ensure that any information about the server is up to date.
+/* Connects a client to the running server, retries until timeout.
* Args:
* client - pointer returned from "cras_client_create".
+ * timeout_ms - timeout in milliseconds or negative to wait forever.
* Returns:
* 0 on success, or a negative error code on failure (from errno.h).
*/
-int cras_client_connected_wait(struct cras_client *client);
+int cras_client_connect_timeout(struct cras_client *client,
+ unsigned int timeout_ms);
-/* Begins running a client.
+/* Begins running the client control thread.
+ *
+ * Required for stream operations and other operations noted below.
+ *
* Args:
* client - the client to start (from cras_client_create).
* Returns:
- * 0 on success, -EINVAL if the client pointer is NULL, or -ENOMEM if there
- * isn't enough memory to start the thread.
+ * 0 on success or if the thread is already running, -EINVAL if the client
+ * pointer is NULL, or the negative result of pthread_create().
*/
int cras_client_run_thread(struct cras_client *client);
/* Stops running a client.
+ * This function is executed automatically by cras_client_destroy().
* Args:
* client - the client to stop (from cras_client_create).
* Returns:
- * 0 on success, -EINVAL if the client isn't valid or isn't running.
+ * 0 on success or if the thread was already stopped, -EINVAL if the client
+ * isn't valid.
*/
int cras_client_stop(struct cras_client *client);
+/* Wait up to 1 second for the client thread to complete the server connection.
+ *
+ * After cras_client_run_thread() is executed, this function can be used to
+ * ensure that the connection has been established with the server and ensure
+ * that any information about the server is up to date. If
+ * cras_client_run_thread() has not yet been executed, or cras_client_stop()
+ * was executed and thread isn't running, then this function returns -EINVAL.
+ *
+ * Args:
+ * client - pointer returned from "cras_client_create".
+ * Returns:
+ * 0 on success, or a negative error code on failure (from errno.h).
+ */
+int cras_client_connected_wait(struct cras_client *client);
+
+/* Ask the client control thread to connect to the audio server.
+ *
+ * After cras_client_run_thread() is executed, this function can be used
+ * to ask the control thread to connect to the audio server asynchronously.
+ * The callback set with cras_client_set_connection_status_cb() will be
+ * executed when the connection is established.
+ *
+ * Args:
+ * client - The client from cras_client_create().
+ * Returns:
+ * 0 on success, or a negative error code on failure (from errno.h).
+ * -EINVAL if the client pointer is invalid or the control thread is
+ * not running.
+ */
+int cras_client_connect_async(struct cras_client *client);
+
+/* Sets server error callback. DEPRECATED
+ *
+ * See cras_server_error_cb_t for more information about this callback.
+ *
+ * Args:
+ * client - The client from cras_client_create.
+ * err_cb - The callback function to register.
+ * user_arg - Pointer that will be passed to the callback.
+ */
+void cras_client_set_server_error_cb(struct cras_client *client,
+ cras_server_error_cb_t err_cb,
+ void *user_arg);
+
+/* Sets server connection status callback.
+ *
+ * See cras_connection_status_t for a description of the connection states
+ * and appropriate user action.
+ *
+ * Args:
+ * client - The client from cras_client_create.
+ * connection_cb - The callback function to register.
+ * user_arg - Pointer that will be passed to the callback.
+ */
+void cras_client_set_connection_status_cb(
+ struct cras_client *client,
+ cras_connection_status_cb_t connection_cb,
+ void *user_arg);
+
+/* Sets callback for setting thread priority.
+ * Args:
+ * client - The client from cras_client_create.
+ * cb - The thread priority callback.
+ */
+void cras_client_set_thread_priority_cb(struct cras_client *client,
+ cras_thread_priority_cb_t cb);
+
/* Returns the current list of output devices.
+ *
+ * Requires that the connection to the server has been established.
+ *
+ * Data is copied and thus can become out of date. This call must be
+ * re-executed to get updates.
+ *
* Args:
* client - The client from cras_client_create.
* devs - Array that will be filled with device info.
@@ -137,6 +328,12 @@ int cras_client_get_output_devices(const struct cras_client *client,
size_t *num_devs, size_t *num_nodes);
/* Returns the current list of input devices.
+ *
+ * Requires that the connection to the server has been established.
+ *
+ * Data is copied and thus can become out of date. This call must be
+ * re-executed to get updates.
+ *
* Args:
* client - The client from cras_client_create.
* devs - Array that will be filled with device info.
@@ -154,6 +351,12 @@ int cras_client_get_input_devices(const struct cras_client *client,
size_t *num_devs, size_t *num_nodes);
/* Returns the current list of clients attached to the server.
+ *
+ * Requires that the connection to the server has been established.
+ *
+ * Data is copied and thus can become out of date. This call must be
+ * re-executed to get updates.
+ *
* Args:
* client - This client (from cras_client_create).
* clients - Array that will be filled with a list of attached clients.
@@ -169,24 +372,36 @@ int cras_client_get_attached_clients(const struct cras_client *client,
/* Find a node info with the matching node id.
*
+ * Requires that the connection to the server has been established.
+ *
+ * Data is copied and thus can become out of date. This call must be
+ * re-executed to get updates.
+ *
* Args:
- * dev_name - The prefix of the iodev name.
- * node_name - The prefix of the ionode name.
- * dev_info - The information about the iodev will be returned here.
+ * client - This client (from cras_client_create).
+ * input - Non-zero for input nodes, zero for output nodes.
+ * node_id - The node id to look for.
* node_info - The information about the ionode will be returned here.
* Returns:
- * 0 if successful, -1 if the node cannot be found.
+ * 0 if successful, negative on error; -ENOENT if the node cannot be found.
*/
int cras_client_get_node_by_id(const struct cras_client *client,
int input,
const cras_node_id_t node_id,
struct cras_ionode_info* node_info);
-/* Checks if the output device with the given name is currently plugged in. For
- * internal devices this checks that jack state, for USB devices this will
- * always be true if they are present. The name parameter can be the
- * complete name or any unique prefix of the name. If the name is not unique
- * the first matching name will be checked.
+/* Checks if the output device with the given name is currently plugged in.
+ *
+ * For internal devices this checks that jack state, for USB devices this will
+ * always be true if they are present. The name parameter can be the complete
+ * name or any unique prefix of the name. If the name is not unique the first
+ * matching name will be checked.
+ *
+ * Requires that the connection to the server has been established.
+ *
+ * Data is copied and thus can become out of date. This call must be
+ * re-executed to get updates.
+ *
* Args:
* client - The client from cras_client_create.
* name - Name of the device to check.
@@ -196,12 +411,15 @@ int cras_client_get_node_by_id(const struct cras_client *client,
int cras_client_output_dev_plugged(const struct cras_client *client,
const char *name);
-/* Sets an attribute of an ionode on a device.
+/* Set the value of an attribute of an ionode.
+ *
* Args:
* client - The client from cras_client_create.
* node_id - The id of the ionode.
* attr - the attribute we want to change.
* value - the value we want to set.
+ * Returns:
+ * Returns 0 for success, negative on error (from errno.h).
*/
int cras_client_set_node_attr(struct cras_client *client,
cras_node_id_t node_id,
@@ -209,6 +427,7 @@ int cras_client_set_node_attr(struct cras_client *client,
int value);
/* Select the preferred node for playback/capture.
+ *
* Args:
* client - The client from cras_client_create.
* direction - The direction of the ionode.
@@ -220,6 +439,7 @@ int cras_client_select_node(struct cras_client *client,
cras_node_id_t node_id);
/* Adds an active node for playback/capture.
+ *
* Args:
* client - The client from cras_client_create.
* direction - The direction of the ionode.
@@ -231,6 +451,7 @@ int cras_client_add_active_node(struct cras_client *client,
cras_node_id_t node_id);
/* Removes an active node for playback/capture.
+ *
* Args:
* client - The client from cras_client_create.
* direction - The direction of the ionode.
@@ -243,6 +464,7 @@ int cras_client_rm_active_node(struct cras_client *client,
/* Asks the server to reload dsp plugin configuration from the ini file.
+ *
* Args:
* client - The client from cras_client_create.
* Returns:
@@ -251,6 +473,7 @@ int cras_client_rm_active_node(struct cras_client *client,
int cras_client_reload_dsp(struct cras_client *client);
/* Asks the server to dump current dsp information to syslog.
+ *
* Args:
* client - The client from cras_client_create.
* Returns:
@@ -259,6 +482,7 @@ int cras_client_reload_dsp(struct cras_client *client);
int cras_client_dump_dsp_info(struct cras_client *client);
/* Asks the server to dump current audio thread information.
+ *
* Args:
* client - The client from cras_client_create.
* cb - A function to call when the data is received.
@@ -328,6 +552,10 @@ struct cras_stream_params *cras_client_unified_params_create(
void cras_client_stream_params_destroy(struct cras_stream_params *params);
/* Creates a new stream and return the stream id or < 0 on error.
+ *
+ * Requires execution of cras_client_run_thread(), and an active connection
+ * to the audio server.
+ *
* Args:
* client - The client to add the stream to (from cras_client_create).
* stream_id_out - On success will be filled with the new stream id.
@@ -342,6 +570,10 @@ int cras_client_add_stream(struct cras_client *client,
struct cras_stream_params *config);
/* Creates a pinned stream and return the stream id or < 0 on error.
+ *
+ * Requires execution of cras_client_run_thread(), and an active connection
+ * to the audio server.
+ *
* Args:
* client - The client to add the stream to (from cras_client_create).
* dev_idx - Index of the device to attach the newly created stream.
@@ -358,6 +590,9 @@ int cras_client_add_pinned_stream(struct cras_client *client,
struct cras_stream_params *config);
/* Removes a currently playing/capturing stream.
+ *
+ * Requires execution of cras_client_run_thread().
+ *
* Args:
* client - Client to remove the stream (returned from cras_client_create).
* stream_id - ID returned from cras_client_add_stream to identify the stream
@@ -369,6 +604,9 @@ int cras_client_rm_stream(struct cras_client *client,
cras_stream_id_t stream_id);
/* Sets the volume scaling factor for the given stream.
+ *
+ * Requires execution of cras_client_run_thread().
+ *
* Args:
* client - Client owning the stream.
* stream_id - ID returned from cras_client_add_stream.
@@ -382,8 +620,11 @@ int cras_client_set_stream_volume(struct cras_client *client,
* System level functions.
*/
-/* Sets the volume of the system. Volume here ranges from 0 to 100, and will be
- * translated to dB based on the output-specific volume curve.
+/* Sets the volume of the system.
+ *
+ * Volume here ranges from 0 to 100, and will be translated to dB based on the
+ * output-specific volume curve.
+ *
* Args:
* client - The client from cras_client_create.
* volume - 0-100 the new volume index.
@@ -393,9 +634,11 @@ int cras_client_set_stream_volume(struct cras_client *client,
*/
int cras_client_set_system_volume(struct cras_client *client, size_t volume);
-/* Sets the capture gain of the system. Gain is specified in dBFS * 100. For
- * example 5dB of gain would be specified with an argument of 500, while -10
- * would be specified with -1000.
+/* Sets the capture gain of the system.
+ *
+ * Gain is specified in dBFS * 100. For example 5dB of gain would be specified
+ * with an argument of 500, while -10 would be specified with -1000.
+ *
* Args:
* client - The client from cras_client_create.
* gain - The gain in dBFS * 100.
@@ -406,6 +649,7 @@ int cras_client_set_system_volume(struct cras_client *client, size_t volume);
int cras_client_set_system_capture_gain(struct cras_client *client, long gain);
/* Sets the mute state of the system.
+ *
* Args:
* client - The client from cras_client_create.
* mute - 0 is un-mute, 1 is muted.
@@ -415,8 +659,10 @@ int cras_client_set_system_capture_gain(struct cras_client *client, long gain);
*/
int cras_client_set_system_mute(struct cras_client *client, int mute);
-/* Sets the user mute state of the system. This is used for mutes caused by
- * user interaction. Like the mute key.
+/* Sets the user mute state of the system.
+ *
+ * This is used for mutes caused by user interaction. Like the mute key.
+ *
* Args:
* client - The client from cras_client_create.
* mute - 0 is un-mute, 1 is muted.
@@ -426,8 +672,10 @@ int cras_client_set_system_mute(struct cras_client *client, int mute);
*/
int cras_client_set_user_mute(struct cras_client *client, int mute);
-/* Sets the mute locked state of the system. Changing mute state is impossible
- * when this flag is set to locked.
+/* Sets the mute locked state of the system.
+ *
+ * Changing mute state is impossible when this flag is set to locked.
+ *
* Args:
* client - The client from cras_client_create.
* locked - 0 is un-locked, 1 is locked.
@@ -437,8 +685,10 @@ int cras_client_set_user_mute(struct cras_client *client, int mute);
*/
int cras_client_set_system_mute_locked(struct cras_client *client, int locked);
-/* Sets the capture mute state of the system. Recordings will be muted when
- * this is set.
+/* Sets the capture mute state of the system.
+ *
+ * Recordings will be muted when this is set.
+ *
* Args:
* client - The client from cras_client_create.
* mute - 0 is un-mute, 1 is muted.
@@ -448,8 +698,10 @@ int cras_client_set_system_mute_locked(struct cras_client *client, int locked);
*/
int cras_client_set_system_capture_mute(struct cras_client *client, int mute);
-/* Sets the capture mute locked state of the system. Changing mute state is
- * impossible when this flag is set to locked.
+/* Sets the capture mute locked state of the system.
+ *
+ * Changing mute state is impossible when this flag is set to locked.
+ *
* Args:
* client - The client from cras_client_create.
* locked - 0 is un-locked, 1 is locked.
@@ -461,44 +713,59 @@ int cras_client_set_system_capture_mute_locked(struct cras_client *client,
int locked);
/* Gets the current system volume.
+ *
+ * Requires that the connection to the server has been established.
+ *
* Args:
* client - The client from cras_client_create.
* Returns:
* The current system volume between 0 and 100.
*/
-size_t cras_client_get_system_volume(struct cras_client *client);
+size_t cras_client_get_system_volume(const struct cras_client *client);
/* Gets the current system capture gain.
+ *
+ * Requires that the connection to the server has been established.
+ *
* Args:
* client - The client from cras_client_create.
* Returns:
* The current system capture volume in dB * 100.
*/
-long cras_client_get_system_capture_gain(struct cras_client *client);
+long cras_client_get_system_capture_gain(const struct cras_client *client);
/* Gets the current system mute state.
+ *
+ * Requires that the connection to the server has been established.
+ *
* Args:
* client - The client from cras_client_create.
* Returns:
* 0 if not muted, 1 if it is.
*/
-int cras_client_get_system_muted(struct cras_client *client);
+int cras_client_get_system_muted(const struct cras_client *client);
/* Gets the current user mute state.
+ *
+ * Requires that the connection to the server has been established.
+ *
* Args:
* client - The client from cras_client_create.
* Returns:
* 0 if not muted, 1 if it is.
*/
-int cras_client_get_user_muted(struct cras_client *client);
+int cras_client_get_user_muted(const struct cras_client *client);
-/* Gets the current system captue mute state.
+/* Gets the current system capture mute state.
+ *
+ * Requires that the connection to the server has been established.
+ *
* Args:
* client - The client from cras_client_create.
* Returns:
* 0 if capture is not muted, 1 if it is.
*/
-int cras_client_get_system_capture_muted(struct cras_client *client);
+int cras_client_get_system_capture_muted(const struct cras_client *client);
/* Gets the current minimum system volume.
* Args:
@@ -507,7 +774,7 @@ int cras_client_get_system_capture_muted(struct cras_client *client);
* The minimum value for the current output device in dBFS * 100. This is
* the level of attenuation at volume == 1.
*/
-long cras_client_get_system_min_volume(struct cras_client *client);
+long cras_client_get_system_min_volume(const struct cras_client *client);
/* Gets the current maximum system volume.
* Args:
@@ -516,25 +783,35 @@ long cras_client_get_system_min_volume(struct cras_client *client);
* The maximum value for the current output device in dBFS * 100. This is
* the level of attenuation at volume == 100.
*/
-long cras_client_get_system_max_volume(struct cras_client *client);
+long cras_client_get_system_max_volume(const struct cras_client *client);
/* Gets the current minimum system capture gain.
+ *
+ * Requires that the connection to the server has been established.
+ *
* Args:
* client - The client from cras_client_create.
* Returns:
* The minimum capture gain for the current input device in dBFS * 100.
*/
-long cras_client_get_system_min_capture_gain(struct cras_client *client);
+long cras_client_get_system_min_capture_gain(const struct cras_client *client);
/* Gets the current maximum system capture gain.
+ *
+ * Requires that the connection to the server has been established.
+ *
* Args:
* client - The client from cras_client_create.
* Returns:
* The maximum capture gain for the current input device in dBFS * 100.
*/
-long cras_client_get_system_max_capture_gain(struct cras_client *client);
+long cras_client_get_system_max_capture_gain(const struct cras_client *client);
/* Gets audio debug info.
+ *
+ * Requires that the connection to the server has been established.
+ * Access to the resulting pointer is not thread-safe.
+ *
* Args:
* client - The client from cras_client_create.
* Returns:
@@ -542,20 +819,24 @@ long cras_client_get_system_max_capture_gain(struct cras_client *client);
* calling cras_client_update_audio_debug_info.
*/
const struct audio_debug_info *cras_client_get_audio_debug_info(
- struct cras_client *client);
+ const struct cras_client *client);
-/* Gets the number of streams currently attached to the server. This is the
- * total number of capture and playback streams. If the ts argument is
- * not null, then it will be filled with the last time audio was played or
- * recorded. ts will be set to the current time if streams are currently
+/* Gets the number of streams currently attached to the server.
+ *
+ * This is the total number of capture and playback streams. If the ts argument
+ * is not null, then it will be filled with the last time audio was played or
+ * recorded. ts will be set to the current time if streams are currently
* active.
+ *
+ * Requires that the connection to the server has been established.
+ *
* Args:
* client - The client from cras_client_create.
* ts - Filled with the timestamp of the last stream.
* Returns:
* The number of active streams.
*/
-unsigned cras_client_get_num_active_streams(struct cras_client *client,
+unsigned cras_client_get_num_active_streams(const struct cras_client *client,
struct timespec *ts);
@@ -598,6 +879,7 @@ int cras_client_calc_capture_latency(const struct timespec *sample_time,
struct timespec *delay);
/* Set the volume of the given output node. Only for output nodes.
+ *
* Args:
* client - The client from cras_client_create.
* node_id - ID of the node.
@@ -608,6 +890,7 @@ int cras_client_set_node_volume(struct cras_client *client,
uint8_t volume);
/* Swap the left and right channel of the given node.
+ *
* Args:
* client - The client from cras_client_create.
* node_id - ID of the node.
@@ -617,6 +900,7 @@ int cras_client_swap_node_left_right(struct cras_client *client,
cras_node_id_t node_id, int enable);
/* Set the capture gain of the given input node. Only for input nodes.
+ *
* Args:
* client - The client from cras_client_create.
* node_id - ID of the node.
@@ -627,6 +911,7 @@ int cras_client_set_node_capture_gain(struct cras_client *client,
long gain);
/* Add a test iodev to the iodev list.
+ *
* Args:
* client - The client from cras_client_create.
* type - The type of test iodev, see cras_types.h
@@ -635,6 +920,7 @@ int cras_client_add_test_iodev(struct cras_client *client,
enum TEST_IODEV_TYPE type);
/* Send a test command to a test iodev.
+ *
* Args:
* client - The client from cras_client_create.
* iodev_idx - The index of the test iodev.
@@ -649,8 +935,11 @@ int cras_client_test_iodev_command(struct cras_client *client,
const uint8_t *data);
/* Finds the first device that contains a node of the given type.
- * This is used for finding a special device list the internal speaker of the
- * AOKR device.
+ *
+ * This is used for finding a special hotword device.
+ *
+ * Requires that the connection to the server has been established.
+ *
* Args:
* client - The client from cras_client_create.
* type - The type of device to find.
@@ -662,13 +951,220 @@ int cras_client_get_first_dev_type_idx(const struct cras_client *client,
enum CRAS_STREAM_DIRECTION direction);
/* Sets the suspend state of audio playback and capture.
+ *
* Set this before putting the system into suspend.
+ *
* Args:
* client - The client from cras_client_create.
* suspend - Suspend the system if non-zero, otherwise resume.
*/
int cras_client_set_suspend(struct cras_client *client, int suspend);
+/* Configures the global converter for output remixing.
+ *
+ * Args:
+ * client - The client from cras_client_create.
+ * num_channels - Number of output channels.
+ * coefficient - Float array representing |num_channels| * |num_channels|
+ * matrix. Channels of mixed PCM output will be remixed by
+ * multiplying this matrix.
+ */
+int cras_client_config_global_remix(struct cras_client *client,
+ unsigned num_channels,
+ float *coefficient);
+
+/* Gets the set of supported hotword language models on a node. The supported
+ * models may differ on different nodes.
+ *
+ * Args:
+ * client - The client from cras_client_create.
+ * node_id - ID of a hotword input node (CRAS_NODE_TYPE_HOTWORD).
+ * cb - The function to be called when hotword models are ready.
+ * Returns:
+ * 0 on success.
+ */
+int cras_client_get_hotword_models(struct cras_client *client,
+ cras_node_id_t node_id,
+ get_hotword_models_cb_t cb);
+
+/* Sets the hotword language model on a node. If there are existing streams on
+ * the hotword input node when this function is called, they need to be closed
+ * then re-opend for the model change to take effect.
+ * Args:
+ * client - The client from cras_client_create.
+ * node_id - ID of a hotword input node (CRAS_NODE_TYPE_HOTWORD).
+ * model_name - Name of the model to use, e.g. "en_us".
+ * Returns:
+ * 0 on success.
+ * -EINVAL if client or node_id is invalid.
+ * -ENOENT if the specified model is not found.
+ */
+int cras_client_set_hotword_model(struct cras_client *client,
+ cras_node_id_t node_id,
+ const char *model_name);
+
+/* Set the context pointer for system state change callbacks.
+ * Args:
+ * client - The client from cras_client_create.
+ * context - The context pointer passed to all callbacks.
+ */
+void cras_client_set_state_change_callback_context(
+ struct cras_client *client, void *context);
+
+/* Output volume change callback.
+ *
+ * Args:
+ * context - Context pointer set with
+ * cras_client_set_state_change_callback_context().
+ * volume - The system output volume, ranging from 0 to 100.
+ */
+typedef void (*cras_client_output_volume_changed_callback)(
+ void* context, int32_t volume);
+
+/* Output mute change callback.
+ *
+ * Args:
+ * context - Context pointer set with
+ * cras_client_set_state_change_callback_context().
+ * muted - Non-zero when the audio is muted, zero otherwise.
+ * user_muted - Non-zero when the audio has been muted by the
+ * user, zero otherwise.
+ * mute_locked - Non-zero when the mute funcion is locked,
+ * zero otherwise.
+ */
+typedef void (*cras_client_output_mute_changed_callback)(
+ void* context, int muted, int user_muted, int mute_locked);
+
+/* Capture gain change callback.
+ *
+ * Args:
+ * context - Context pointer set with
+ * cras_client_set_state_change_callback_context().
+ * gain - The system capture gain, in centi-decibels.
+ */
+typedef void (*cras_client_capture_gain_changed_callback)(
+ void* context, int32_t gain);
+
+/* Capture mute change callback.
+ *
+ * Args:
+ * context - Context pointer set with
+ * cras_client_set_state_change_callback_context().
+ * muted - Non-zero when the audio is muted, zero otherwise.
+ * mute_locked - Non-zero when the mute funcion is locked,
+ * zero otherwise.
+ */
+typedef void (*cras_client_capture_mute_changed_callback)(
+ void* context, int muted, int mute_locked);
+
+/* Nodes change callback.
+ *
+ * Args:
+ * context - Context pointer set with
+ * cras_client_set_state_change_callback_context().
+ */
+typedef void (*cras_client_nodes_changed_callback)(void* context);
+
+/* Active node change callback.
+ *
+ * Args:
+ * context - Context pointer set with
+ * cras_client_set_state_change_callback_context().
+ * direction - Indicates the direction of the selected node.
+ * node_id - The ID of the selected node. Special device ID values
+ * defined by CRAS_SPECIAL_DEVICE will be used when no other
+ * device or node is selected or between selections.
+ */
+typedef void (*cras_client_active_node_changed_callback)(
+ void* context, enum CRAS_STREAM_DIRECTION direction,
+ cras_node_id_t node_id);
+
+/* Output node volume change callback.
+ *
+ * Args:
+ * context - Context pointer set with
+ * cras_client_set_state_change_callback_context().
+ * node_id - The ID of the output node.
+ * volume - The volume for this node with range 0 to 100.
+ */
+typedef void (*cras_client_output_node_volume_changed_callback)(
+ void* context, cras_node_id_t node_id, int32_t volume);
+
+/* Node left right swapped change callback.
+ *
+ * Args:
+ * context - Context pointer set with
+ * cras_client_set_state_change_callback_context().
+ * node_id - The ID of the node.
+ * swapped - Non-zero if the node is left-right swapped, zero otherwise.
+ */
+typedef void (*cras_client_node_left_right_swapped_changed_callback)(
+ void* context, cras_node_id_t node_id, int swapped);
+
+/* Input node gain change callback.
+ * Args:
+ * context - Context pointer set with
+ * cras_client_set_state_change_callback_context().
+ * node_id - The ID of the input node.
+ * gain - The gain for this node in centi-decibels.
+ */
+typedef void (*cras_client_input_node_gain_changed_callback)(
+ void* context, cras_node_id_t node_id, int32_t gain);
+
+/* Number of active streams change callback.
+ *
+ * Args:
+ * context - Context pointer set with
+ * cras_client_set_state_change_callback_context().
+ * direction - Indicates the direction of the stream's node.
+ * num_active_streams - The number of active streams.
+ */
+typedef void (*cras_client_num_active_streams_changed_callback)(
+ void* context, enum CRAS_STREAM_DIRECTION direction,
+ uint32_t num_active_streams);
+
+/* Set system state information callbacks.
+ * NOTE: These callbacks are executed from the client control thread.
+ * Each state change callback is given the context pointer set with
+ * cras_client_set_state_change_callback_context(). The context pointer is
+ * NULL by default.
+ * Args:
+ * client - The client from cras_client_create.
+ * cb - The callback, or NULL to disable the call-back.
+ * Returns:
+ * 0 for success or negative errno error code on error.
+ */
+int cras_client_set_output_volume_changed_callback(
+ struct cras_client *client,
+ cras_client_output_volume_changed_callback cb);
+int cras_client_set_output_mute_changed_callback(
+ struct cras_client *client,
+ cras_client_output_mute_changed_callback cb);
+int cras_client_set_capture_gain_changed_callback(
+ struct cras_client *client,
+ cras_client_capture_gain_changed_callback cb);
+int cras_client_set_capture_mute_changed_callback(
+ struct cras_client *client,
+ cras_client_capture_mute_changed_callback cb);
+int cras_client_set_nodes_changed_callback(
+ struct cras_client *client,
+ cras_client_nodes_changed_callback cb);
+int cras_client_set_active_node_changed_callback(
+ struct cras_client *client,
+ cras_client_active_node_changed_callback cb);
+int cras_client_set_output_node_volume_changed_callback(
+ struct cras_client *client,
+ cras_client_output_node_volume_changed_callback cb);
+int cras_client_set_node_left_right_swapped_changed_callback(
+ struct cras_client *client,
+ cras_client_node_left_right_swapped_changed_callback cb);
+int cras_client_set_input_node_gain_changed_callback(
+ struct cras_client *client,
+ cras_client_input_node_gain_changed_callback cb);
+int cras_client_set_num_active_streams_changed_callback(
+ struct cras_client *client,
+ cras_client_num_active_streams_changed_callback cb);
+
#ifdef __cplusplus
}
#endif
diff --git a/cras/src/libcras/cras_helpers.c b/cras/src/libcras/cras_helpers.c
index 9312a94d..3db8f6af 100644
--- a/cras/src/libcras/cras_helpers.c
+++ b/cras/src/libcras/cras_helpers.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015 The Chromium OS Authors. All rights reserved.
+/* Copyright 2015 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.
*/
@@ -52,6 +52,33 @@ static int play_buffer_error(struct cras_client *client,
return 0;
}
+int cras_helper_create_connect_async(struct cras_client **client,
+ cras_connection_status_cb_t connection_cb,
+ void *user_arg)
+{
+ int rc;
+
+ rc = cras_client_create(client);
+ if (rc < 0)
+ return rc;
+
+ cras_client_set_connection_status_cb(*client, connection_cb, user_arg);
+
+ rc = cras_client_run_thread(*client);
+ if (rc < 0)
+ goto client_start_error;
+
+ rc = cras_client_connect_async(*client);
+ if (rc < 0)
+ goto client_start_error;
+
+ return 0;
+
+client_start_error:
+ cras_client_destroy(*client);
+ return rc;
+}
+
int cras_helper_create_connect(struct cras_client **client)
{
int rc;
diff --git a/cras/src/libcras/cras_helpers.h b/cras/src/libcras/cras_helpers.h
index e9343c0c..ad54c5cf 100644
--- a/cras/src/libcras/cras_helpers.h
+++ b/cras/src/libcras/cras_helpers.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015 The Chromium OS Authors. All rights reserved.
+/* Copyright 2015 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.
*/
@@ -9,7 +9,27 @@
extern "C" {
#endif
+/* Creates and connects a client to the running server asynchronously.
+ *
+ * When the connection has been established the connection_cb is executed
+ * with the appropriate state. See cras_connection_status_cb_t for more
+ * information.
+ *
+ * Args:
+ * client - Filled with a pointer to the new client.
+ * connection_cb - The connection status callback function.
+ * user_arg - Argument passed to the connection status callback.
+ * Returns:
+ * 0 on success, or a negative error code on failure (from errno.h).
+ */
+int cras_helper_create_connect_async(struct cras_client **client,
+ cras_connection_status_cb_t connection_cb,
+ void *user_arg);
+
/* Creates and connects a client to the running server.
+ *
+ * Waits forever (or interrupt) for the server to be available.
+ *
* Args:
* client - Filled with a pointer to the new client.
* Returns:
diff --git a/cras/src/server/audio_thread.c b/cras/src/server/audio_thread.c
index 2422e487..8b349146 100644
--- a/cras/src/server/audio_thread.c
+++ b/cras/src/server/audio_thread.c
@@ -42,6 +42,9 @@ enum AUDIO_THREAD_COMMAND {
AUDIO_THREAD_STOP,
AUDIO_THREAD_DUMP_THREAD_INFO,
AUDIO_THREAD_DRAIN_STREAM,
+ AUDIO_THREAD_CONFIG_GLOBAL_REMIX,
+ AUDIO_THREAD_DEV_START_RAMP,
+ AUDIO_THREAD_REMOVE_CALLBACK,
};
struct audio_thread_msg {
@@ -49,11 +52,21 @@ struct audio_thread_msg {
enum AUDIO_THREAD_COMMAND id;
};
+struct audio_thread_config_global_remix {
+ struct audio_thread_msg header;
+ struct cras_fmt_conv *fmt_conv;
+};
+
struct audio_thread_open_device_msg {
struct audio_thread_msg header;
struct cras_iodev *dev;
};
+struct audio_thread_rm_callback_msg {
+ struct audio_thread_msg header;
+ int fd;
+};
+
struct audio_thread_add_rm_stream_msg {
struct audio_thread_msg header;
struct cras_rstream *stream;
@@ -66,8 +79,16 @@ struct audio_thread_dump_debug_info_msg {
struct audio_debug_info *info;
};
+struct audio_thread_dev_start_ramp_msg {
+ struct audio_thread_msg header;
+ struct cras_iodev *dev;
+ enum CRAS_IODEV_RAMP_REQUEST request;
+};
+
/* Audio thread logging. */
struct audio_thread_event_log *atlog;
+/* Global fmt converter used to remix output channels. */
+static struct cras_fmt_conv *remix_converter = NULL;
static struct iodev_callback_list *iodev_callbacks;
static struct timespec longest_wake;
@@ -184,36 +205,10 @@ static int audio_thread_read_command(struct audio_thread *thread,
return 0;
}
-/* Put 'frames' worth of zero samples into odev. */
-static int fill_odev_zeros(struct cras_iodev *odev, unsigned int frames)
-{
- struct cras_audio_area *area = NULL;
- unsigned int frame_bytes, frames_written;
- int rc;
- uint8_t *buf;
-
- frame_bytes = cras_get_format_bytes(odev->ext_format);
- while (frames > 0) {
- frames_written = frames;
- rc = cras_iodev_get_output_buffer(odev, &area, &frames_written);
- if (rc < 0) {
- syslog(LOG_ERR, "fill zeros fail: %d", rc);
- return rc;
- }
- /* This assumes consecutive channel areas. */
- buf = area->channels[0].buf;
- memset(buf, 0, frames_written * frame_bytes);
- cras_iodev_put_output_buffer(odev, buf, frames_written);
- frames -= frames_written;
- }
-
- return 0;
-}
-
/* Builds an initial buffer to avoid an underrun. Adds min_level of latency. */
static void fill_odevs_zeros_min_level(struct cras_iodev *odev)
{
- fill_odev_zeros(odev, odev->min_buffer_level);
+ cras_iodev_fill_odev_zeros(odev, odev->min_buffer_level);
}
static void thread_rm_open_adev(struct audio_thread *thread,
@@ -238,6 +233,8 @@ static int append_stream(struct audio_thread *thread,
struct open_dev *open_dev;
struct cras_iodev *dev;
struct dev_stream *out;
+ struct timespec init_cb_ts;
+ const struct timespec *stream_ts;
unsigned int i;
int rc = 0;
@@ -252,8 +249,19 @@ static int append_stream(struct audio_thread *thread,
if (out)
continue;
+ /* If open device already has stream, get the first stream
+ * and use its next callback time to align with. Otherwise
+ * use the timestamp now as the initial callback time for
+ * new stream.
+ */
+ if (dev->streams &&
+ (stream_ts = dev_stream_next_cb_ts(dev->streams)))
+ init_cb_ts = *stream_ts;
+ else
+ clock_gettime(CLOCK_MONOTONIC_RAW, &init_cb_ts);
+
out = dev_stream_create(stream, dev->info.idx,
- dev->ext_format, dev);
+ dev->ext_format, dev, &init_cb_ts);
if (!out) {
rc = -EINVAL;
break;
@@ -392,6 +400,20 @@ static int thread_rm_open_dev(struct audio_thread *thread,
return 0;
}
+/* Handles messages from the main thread to start ramping on a device. */
+static int thread_dev_start_ramp(struct audio_thread *thread,
+ struct cras_iodev *iodev,
+ enum CRAS_IODEV_RAMP_REQUEST request)
+{
+ /* Do nothing if device wasn't already in the active dev list. */
+ struct open_dev *adev = find_adev(
+ thread->open_devs[iodev->direction], iodev);
+ if (!adev)
+ return -EINVAL;
+ return cras_iodev_start_ramp(iodev, request);
+}
+
+
/* Return non-zero if the stream is attached to any device. */
static int thread_find_stream(struct audio_thread *thread,
struct cras_rstream *rstream)
@@ -572,8 +594,13 @@ static int fetch_streams(struct audio_thread *thread,
const struct timespec *next_cb_ts;
struct timespec now;
- if (cras_shm_callback_pending(shm) && fd >= 0)
+ clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+
+ if (cras_shm_callback_pending(shm) && fd >= 0) {
flush_old_aud_messages(shm, fd);
+ cras_rstream_record_fetch_interval(dev_stream->stream,
+ &now);
+ }
if (cras_shm_get_frames(shm) < 0)
cras_rstream_set_is_draining(rstream, 1);
@@ -587,7 +614,6 @@ static int fetch_streams(struct audio_thread *thread,
/* Check if it's time to get more data from this stream.
* Allowing for waking up half a little early. */
- clock_gettime(CLOCK_MONOTONIC_RAW, &now);
add_timespecs(&now, &playback_wake_fuzz_ts);
if (!timespec_after(&now, next_cb_ts))
continue;
@@ -749,6 +775,9 @@ static void append_dev_dump_info(struct audio_dev_debug_info *di,
di->min_cb_level = adev->dev->min_cb_level;
di->max_cb_level = adev->dev->max_cb_level;
di->direction = adev->dev->direction;
+ di->num_underruns = cras_iodev_get_num_underruns(adev->dev);
+ di->num_severe_underruns = cras_iodev_get_num_severe_underruns(
+ adev->dev);
if (fmt) {
di->frame_rate = fmt->frame_rate;
di->num_channels = fmt->num_channels;
@@ -773,6 +802,7 @@ static void append_stream_dump_info(struct audio_debug_info *info,
si->stream_id = stream->stream->stream_id;
si->dev_idx = dev_idx;
si->direction = stream->stream->direction;
+ si->stream_type = stream->stream->stream_type;
si->buffer_frames = stream->stream->buffer_frames;
si->cb_threshold = stream->stream->cb_threshold;
si->frame_rate = stream->stream->format.frame_rate;
@@ -781,6 +811,7 @@ static void append_stream_dump_info(struct audio_debug_info *info,
sizeof(si->channel_layout));
si->longest_fetch_sec = stream->stream->longest_fetch_interval.tv_sec;
si->longest_fetch_nsec = stream->stream->longest_fetch_interval.tv_nsec;
+ si->num_overruns = cras_shm_num_overruns(&stream->stream->shm);
longest_wake.tv_sec = 0;
longest_wake.tv_nsec = 0;
@@ -894,6 +925,33 @@ static int handle_playback_thread_message(struct audio_thread *thread)
ret = thread_drain_stream(thread, rmsg->stream);
break;
}
+ case AUDIO_THREAD_REMOVE_CALLBACK: {
+ struct audio_thread_rm_callback_msg *rmsg;
+
+ rmsg = (struct audio_thread_rm_callback_msg *)msg;
+ audio_thread_rm_callback(rmsg->fd);
+ break;
+ }
+ case AUDIO_THREAD_CONFIG_GLOBAL_REMIX: {
+ struct audio_thread_config_global_remix *rmsg;
+ void *rsp;
+
+ /* Respond the pointer to the old remix converter, so it can be
+ * freed later in main thread. */
+ rsp = (void *)remix_converter;
+
+ rmsg = (struct audio_thread_config_global_remix *)msg;
+ remix_converter = rmsg->fmt_conv;
+
+ return write(thread->to_main_fds[1], &rsp, sizeof(rsp));
+ }
+ case AUDIO_THREAD_DEV_START_RAMP: {
+ struct audio_thread_dev_start_ramp_msg *rmsg;
+
+ rmsg = (struct audio_thread_dev_start_ramp_msg*)msg;
+ ret = thread_dev_start_ramp(thread, rmsg->dev, rmsg->request);
+ break;
+ }
default:
ret = -EINVAL;
break;
@@ -945,10 +1003,9 @@ static int get_next_output_wake(struct audio_thread *thread,
struct open_dev *adev;
struct timespec sleep_time;
double est_rate;
- unsigned int hw_level;
int ret = 0;
- int rc;
- int frames_to_play_in_sleep;
+ unsigned int frames_to_play_in_sleep;
+ unsigned int hw_level = 0;
DL_FOREACH(thread->open_devs[CRAS_STREAM_OUTPUT], adev)
ret += get_next_stream_wake_from_list(
@@ -956,33 +1013,17 @@ static int get_next_output_wake(struct audio_thread *thread,
min_ts);
DL_FOREACH(thread->open_devs[CRAS_STREAM_OUTPUT], adev) {
- rc = cras_iodev_frames_queued(adev->dev);
- hw_level = (rc < 0) ? 0 : rc;
+ if (!cras_iodev_odev_should_wake(adev->dev))
+ continue;
- adev->wake_ts = *now;
+ frames_to_play_in_sleep = cras_iodev_frames_to_play_in_sleep(
+ adev->dev, &hw_level, &adev->wake_ts);
+ if (!timespec_is_nonzero(&adev->wake_ts))
+ adev->wake_ts = *now;
est_rate = adev->dev->ext_format->frame_rate *
cras_iodev_get_est_rate_ratio(adev->dev);
-
- if (adev->dev->streams)
- /* Use the estimated dev rate to schedule that audio
- * thread will wake up when hw_level drops to 0.
- */
- frames_to_play_in_sleep = hw_level;
- else {
- /* When device has no stream and we fill zeros,
- * use the estimated dev rate to schedule that audio
- * thread will wake up when hw_level drops to
- * min_cb_level.
- */
- if (hw_level > adev->dev->min_cb_level)
- frames_to_play_in_sleep = hw_level -
- adev->dev->min_cb_level;
- else
- frames_to_play_in_sleep = 0;
- }
-
ATLOG(atlog,
AUDIO_THREAD_SET_DEV_WAKE,
adev->dev->info.idx,
@@ -1017,7 +1058,7 @@ static int input_adev_ignore_wake(const struct open_dev *adev)
if (!adev->dev->active_node)
return 1;
- if (adev->dev->active_node->type == CRAS_NODE_TYPE_AOKR &&
+ if (adev->dev->active_node->type == CRAS_NODE_TYPE_HOTWORD &&
!adev->input_streaming)
return 1;
@@ -1047,38 +1088,6 @@ static int get_next_input_wake(struct audio_thread *thread,
return ret;
}
-/* When an odev is open but no streams are attached, play zeros.
- * Args:
- * odev - the output device to be filled.
- */
-int fill_output_no_streams(struct open_dev *adev)
-{
- unsigned int hw_level;
- unsigned int fr_to_write;
- int rc;
- struct cras_iodev *odev = adev->dev;
- unsigned int target_hw_level = odev->min_cb_level * 2;
-
- rc = cras_iodev_frames_queued(odev);
- if (rc < 0)
- return rc;
- hw_level = rc;
-
- fr_to_write = cras_iodev_buffer_avail(odev, hw_level);
-
- if (hw_level <= target_hw_level)
- fill_odev_zeros(odev, MIN(target_hw_level - hw_level,
- fr_to_write));
-
- ATLOG(atlog,
- AUDIO_THREAD_ODEV_NO_STREAMS,
- odev->info.idx,
- hw_level,
- odev->min_cb_level);
-
- return 0;
-}
-
static int output_stream_fetch(struct audio_thread *thread)
{
struct open_dev *odev_list = thread->open_devs[CRAS_STREAM_OUTPUT];
@@ -1137,6 +1146,7 @@ static int write_output_samples(struct audio_thread *thread,
{
struct cras_iodev *odev = adev->dev;
unsigned int hw_level;
+ struct timespec hw_tstamp;
unsigned int frames, fr_to_req;
snd_pcm_sframes_t written;
snd_pcm_uframes_t total_written = 0;
@@ -1144,23 +1154,35 @@ static int write_output_samples(struct audio_thread *thread,
uint8_t *dst = NULL;
struct cras_audio_area *area = NULL;
- if (!odev->streams)
- return fill_output_no_streams(adev);
+ /* Possibly fill zeros for no_stream state and possibly transit state.
+ */
+ rc = cras_iodev_prepare_output_before_write_samples(odev);
+ if (rc < 0) {
+ syslog(LOG_ERR, "Failed to prepare output dev for write");
+ return rc;
+ }
- rc = cras_iodev_frames_queued(odev);
+ if (cras_iodev_state(odev) != CRAS_IODEV_STATE_NORMAL_RUN)
+ return 0;
+
+ rc = cras_iodev_frames_queued(odev, &hw_tstamp);
if (rc < 0)
return rc;
hw_level = rc;
- if (hw_level < odev->min_cb_level / 2)
- adev->coarse_rate_adjust = 1;
- else if (hw_level > odev->max_cb_level * 2)
- adev->coarse_rate_adjust = -1;
- else
- adev->coarse_rate_adjust = 0;
- if (cras_iodev_update_rate(odev, hw_level))
- update_estimated_rate(thread, adev);
+ ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO_TSTAMP, adev->dev->info.idx,
+ hw_tstamp.tv_sec, hw_tstamp.tv_nsec);
+ if (timespec_is_nonzero(&hw_tstamp)) {
+ if (hw_level < odev->min_cb_level / 2)
+ adev->coarse_rate_adjust = 1;
+ else if (hw_level > odev->max_cb_level * 2)
+ adev->coarse_rate_adjust = -1;
+ else
+ adev->coarse_rate_adjust = 0;
+ if (cras_iodev_update_rate(odev, hw_level, &hw_tstamp))
+ update_estimated_rate(thread, adev);
+ }
ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO,
adev->dev->info.idx, hw_level, 0);
@@ -1195,17 +1217,13 @@ static int write_output_samples(struct audio_thread *thread,
total_written += written;
}
- /* If we haven't started the device and wrote samples, then start it. */
- if (total_written || hw_level) {
- if (!odev->dev_running(odev))
- return -1;
- } else if (odev->min_cb_level < odev->buffer_size) {
- /* Empty hardware and nothing written, zero fill it. */
- fill_odev_zeros(adev->dev, odev->min_cb_level);
- }
+ /* Empty hardware and nothing written, zero fill it if it is running. */
+ if (!hw_level && !total_written &&
+ odev->min_cb_level < odev->buffer_size)
+ cras_iodev_output_underrun(odev);
ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO_DONE,
- total_written, 0, 0);
+ hw_level, total_written, odev->min_cb_level);
return 0;
}
@@ -1231,8 +1249,15 @@ static int do_playback(struct audio_thread *thread)
rc = write_output_samples(thread, adev);
if (rc < 0) {
- /* Device error, close it. */
- thread_rm_open_adev(thread, adev);
+ if (rc == -EPIPE) {
+ /* Handle severe underrun. */
+ ATLOG(atlog, AUDIO_THREAD_SEVERE_UNDERRUN,
+ adev->dev->info.idx, 0, 0);
+ cras_iodev_reset_request(adev->dev);
+ } else {
+ /* Device error, close it. */
+ thread_rm_open_adev(thread, adev);
+ }
}
}
@@ -1261,6 +1286,7 @@ static unsigned int get_stream_limit_set_delay(struct open_dev *adev,
struct cras_audio_shm *shm;
struct dev_stream *stream;
int delay;
+ unsigned int avail;
/* TODO(dgreid) - Setting delay from last dev only. */
delay = input_delay_frames(adev);
@@ -1269,10 +1295,13 @@ static unsigned int get_stream_limit_set_delay(struct open_dev *adev,
rstream = stream->stream;
shm = cras_rstream_input_shm(rstream);
- cras_shm_check_write_overrun(shm);
+ if (cras_shm_check_write_overrun(shm))
+ ATLOG(atlog, AUDIO_THREAD_READ_OVERRUN,
+ adev->dev->info.idx, rstream->stream_id,
+ shm->area->num_overruns);
dev_stream_set_delay(stream, delay);
- write_limit = MIN(write_limit,
- dev_stream_capture_avail(stream));
+ avail = dev_stream_capture_avail(stream);
+ write_limit = MIN(write_limit, avail);
}
return write_limit;
@@ -1281,41 +1310,44 @@ static unsigned int get_stream_limit_set_delay(struct open_dev *adev,
/* Read samples from an input device to the specified stream.
* Args:
* adev - The device to capture samples from.
- * dev_index - The index of the device being read from, only used to special
- * case the first read.
* Returns 0 on success.
*/
static int capture_to_streams(struct audio_thread *thread,
- struct open_dev *adev,
- unsigned int dev_index)
+ struct open_dev *adev)
{
struct cras_iodev *idev = adev->dev;
- snd_pcm_uframes_t remainder, hw_level;
+ snd_pcm_uframes_t remainder, hw_level, cap_limit;
+ struct timespec hw_tstamp;
int rc;
- rc = cras_iodev_frames_queued(idev);
+ rc = cras_iodev_frames_queued(idev, &hw_tstamp);
if (rc < 0)
return rc;
hw_level = rc;
- if (hw_level < idev->min_cb_level / 2)
- adev->coarse_rate_adjust = 1;
- else if (hw_level > idev->max_cb_level * 2)
- adev->coarse_rate_adjust = -1;
- else
- adev->coarse_rate_adjust = 0;
- if (hw_level)
- adev->input_streaming = 1;
+ ATLOG(atlog, AUDIO_THREAD_READ_AUDIO_TSTAMP, idev->info.idx,
+ hw_tstamp.tv_sec, hw_tstamp.tv_nsec);
+ if (timespec_is_nonzero(&hw_tstamp)) {
+ if (hw_level)
+ adev->input_streaming = 1;
- if (cras_iodev_update_rate(idev, hw_level))
- update_estimated_rate(thread, adev);
+ if (hw_level < idev->min_cb_level / 2)
+ adev->coarse_rate_adjust = 1;
+ else if (hw_level > idev->max_cb_level * 2)
+ adev->coarse_rate_adjust = -1;
+ else
+ adev->coarse_rate_adjust = 0;
+ if (cras_iodev_update_rate(idev, hw_level, &hw_tstamp))
+ update_estimated_rate(thread, adev);
+ }
- remainder = MIN(hw_level, get_stream_limit_set_delay(adev, hw_level));
+ cap_limit = get_stream_limit_set_delay(adev, hw_level);
+ remainder = MIN(hw_level, cap_limit);
ATLOG(atlog, AUDIO_THREAD_READ_AUDIO,
idev->info.idx, hw_level, remainder);
- if (!idev->dev_running(idev))
+ if (cras_iodev_state(idev) != CRAS_IODEV_STATE_NORMAL_RUN)
return 0;
while (remainder > 0) {
@@ -1334,8 +1366,10 @@ static int capture_to_streams(struct audio_thread *thread,
unsigned int area_offset;
area_offset = cras_iodev_stream_offset(idev, stream);
- this_read = dev_stream_capture(stream, area,
- area_offset, dev_index);
+ this_read = dev_stream_capture(
+ stream, area, area_offset,
+ cras_iodev_get_software_gain_scaler(idev));
+
cras_iodev_stream_written(idev, stream, this_read);
}
if (adev->dev->streams)
@@ -1362,52 +1396,82 @@ static int do_capture(struct audio_thread *thread)
{
struct open_dev *idev_list = thread->open_devs[CRAS_STREAM_INPUT];
struct open_dev *adev;
- unsigned int dev_index = 0;
DL_FOREACH(idev_list, adev) {
if (!cras_iodev_is_open(adev->dev))
continue;
- if (capture_to_streams(thread, adev, dev_index) < 0)
+ if (capture_to_streams(thread, adev) < 0)
thread_rm_open_adev(thread, adev);
- dev_index++;
}
return 0;
}
+/*
+ * Set wake_ts for this device to be the earliest wake up time for
+ * dev_streams.
+ */
+static int set_input_dev_wake_ts(struct open_dev *adev)
+{
+ int rc;
+ struct timespec level_tstamp, wake_time_out, min_ts, now;
+ unsigned int curr_level;
+ struct dev_stream *stream;
+
+ /* Limit the sleep time to 20 seconds. */
+ min_ts.tv_sec = 20;
+ min_ts.tv_nsec = 0;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+ add_timespecs(&min_ts, &now);
+
+ curr_level = cras_iodev_frames_queued(adev->dev, &level_tstamp);
+ if (!timespec_is_nonzero(&level_tstamp))
+ clock_gettime(CLOCK_MONOTONIC_RAW, &level_tstamp);
+
+ /*
+ * Loop through streams to find the earliest time audio thread
+ * should wake up.
+ */
+ DL_FOREACH(adev->dev->streams, stream) {
+ rc = dev_stream_wake_time(
+ stream,
+ curr_level,
+ &level_tstamp,
+ &wake_time_out);
+
+ if (rc < 0)
+ return rc;
+
+ if (timespec_after(&min_ts, &wake_time_out)) {
+ min_ts = wake_time_out;
+ }
+ }
+ adev->wake_ts = min_ts;
+ return 0;
+}
+
static int send_captured_samples(struct audio_thread *thread)
{
struct open_dev *idev_list = thread->open_devs[CRAS_STREAM_INPUT];
struct open_dev *adev;
- struct timespec now;
+ int rc;
// TODO(dgreid) - once per rstream, not once per dev_stream.
DL_FOREACH(idev_list, adev) {
struct dev_stream *stream;
- unsigned int min_needed = adev->dev->max_cb_level;
- unsigned int curr_level;
if (!cras_iodev_is_open(adev->dev))
continue;
- curr_level = cras_iodev_frames_queued(adev->dev);
-
+ /* Post samples to rstream if there are enough samples. */
DL_FOREACH(adev->dev->streams, stream) {
dev_stream_capture_update_rstream(stream);
- min_needed = MIN(min_needed,
- dev_stream_capture_avail(stream));
}
- if (min_needed > curr_level)
- min_needed -= curr_level;
- else
- min_needed = 0;
-
- clock_gettime(CLOCK_MONOTONIC_RAW, &now);
- cras_frames_to_time(min_needed + 10,
- adev->dev->ext_format->frame_rate,
- &adev->wake_ts);
- add_timespecs(&adev->wake_ts, &now);
+ /* Set wake_ts for this device. */
+ rc = set_input_dev_wake_ts(adev);
+ if (rc < 0)
+ return rc;
}
return 0;
@@ -1587,7 +1651,8 @@ restart_poll_loop:
static int audio_thread_post_message(struct audio_thread *thread,
struct audio_thread_msg *msg)
{
- int rc, err;
+ int err;
+ void *rsp;
err = write(thread->to_thread_fds[1], msg, msg->length);
if (err < 0) {
@@ -1595,13 +1660,68 @@ static int audio_thread_post_message(struct audio_thread *thread,
return err;
}
/* Synchronous action, wait for response. */
- err = read(thread->to_main_fds[0], &rc, sizeof(rc));
+ err = read(thread->to_main_fds[0], &rsp, sizeof(rsp));
if (err < 0) {
syslog(LOG_ERR, "Failed to read reply from thread.");
return err;
}
- return rc;
+ return (intptr_t)rsp;
+}
+
+static void init_open_device_msg(struct audio_thread_open_device_msg *msg,
+ enum AUDIO_THREAD_COMMAND id,
+ struct cras_iodev *dev)
+{
+ memset(msg, 0, sizeof(*msg));
+ msg->header.id = id;
+ msg->header.length = sizeof(*msg);
+ msg->dev = dev;
+}
+
+static void init_add_rm_stream_msg(struct audio_thread_add_rm_stream_msg *msg,
+ enum AUDIO_THREAD_COMMAND id,
+ struct cras_rstream *stream,
+ struct cras_iodev **devs,
+ unsigned int num_devs)
+{
+ memset(msg, 0, sizeof(*msg));
+ msg->header.id = id;
+ msg->header.length = sizeof(*msg);
+ msg->stream = stream;
+ msg->devs = devs;
+ msg->num_devs = num_devs;
+}
+
+static void init_dump_debug_info_msg(
+ struct audio_thread_dump_debug_info_msg *msg,
+ struct audio_debug_info *info)
+{
+ memset(msg, 0, sizeof(*msg));
+ msg->header.id = AUDIO_THREAD_DUMP_THREAD_INFO;
+ msg->header.length = sizeof(*msg);
+ msg->info = info;
+}
+
+static void init_config_global_remix_msg(
+ struct audio_thread_config_global_remix *msg)
+{
+ memset(msg, 0, sizeof(*msg));
+ msg->header.id = AUDIO_THREAD_CONFIG_GLOBAL_REMIX;
+ msg->header.length = sizeof(*msg);
+}
+
+static void init_device_start_ramp_msg(
+ struct audio_thread_dev_start_ramp_msg *msg,
+ enum AUDIO_THREAD_COMMAND id,
+ struct cras_iodev *dev,
+ enum CRAS_IODEV_RAMP_REQUEST request)
+{
+ memset(msg, 0, sizeof(*msg));
+ msg->header.id = id;
+ msg->header.length = sizeof(*msg);
+ msg->dev = dev;
+ msg->request = request;
}
/* Exported Interface */
@@ -1618,11 +1738,8 @@ int audio_thread_add_stream(struct audio_thread *thread,
if (!thread->started)
return -EINVAL;
- msg.header.id = AUDIO_THREAD_ADD_STREAM;
- msg.header.length = sizeof(struct audio_thread_add_rm_stream_msg);
- msg.stream = stream;
- msg.devs = devs;
- msg.num_devs = num_devs;
+ init_add_rm_stream_msg(&msg, AUDIO_THREAD_ADD_STREAM, stream,
+ devs, num_devs);
return audio_thread_post_message(thread, &msg.header);
}
@@ -1634,10 +1751,8 @@ int audio_thread_disconnect_stream(struct audio_thread *thread,
assert(thread && stream);
- msg.header.id = AUDIO_THREAD_DISCONNECT_STREAM;
- msg.header.length = sizeof(struct audio_thread_add_rm_stream_msg);
- msg.stream = stream;
- msg.devs = &dev;
+ init_add_rm_stream_msg(&msg, AUDIO_THREAD_DISCONNECT_STREAM, stream,
+ &dev, 0);
return audio_thread_post_message(thread, &msg.header);
}
@@ -1648,9 +1763,8 @@ int audio_thread_drain_stream(struct audio_thread *thread,
assert(thread && stream);
- msg.header.id = AUDIO_THREAD_DRAIN_STREAM;
- msg.header.length = sizeof(struct audio_thread_add_rm_stream_msg);
- msg.stream = stream;
+ init_add_rm_stream_msg(&msg, AUDIO_THREAD_DRAIN_STREAM, stream,
+ NULL, 0);
return audio_thread_post_message(thread, &msg.header);
}
@@ -1659,12 +1773,78 @@ int audio_thread_dump_thread_info(struct audio_thread *thread,
{
struct audio_thread_dump_debug_info_msg msg;
- msg.header.id = AUDIO_THREAD_DUMP_THREAD_INFO;
+ init_dump_debug_info_msg(&msg, info);
+ return audio_thread_post_message(thread, &msg.header);
+}
+
+int audio_thread_rm_callback_sync(struct audio_thread *thread, int fd) {
+ struct audio_thread_rm_callback_msg msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.header.id = AUDIO_THREAD_REMOVE_CALLBACK;
msg.header.length = sizeof(msg);
- msg.info = info;
+ msg.fd = fd;
+
return audio_thread_post_message(thread, &msg.header);
}
+int audio_thread_config_global_remix(struct audio_thread *thread,
+ unsigned int num_channels,
+ const float *coefficient)
+{
+ int err;
+ int identity_remix = 1;
+ unsigned int i, j;
+ struct audio_thread_config_global_remix msg;
+ void *rsp;
+
+ init_config_global_remix_msg(&msg);
+
+ /* Check if the coefficients represent an identity matrix for remix
+ * conversion, which means no remix at all. If so then leave the
+ * converter as NULL. */
+ for (i = 0; i < num_channels; i++) {
+ if (coefficient[i * num_channels + i] != 1.0f) {
+ identity_remix = 0;
+ break;
+ }
+ for (j = i + 1; j < num_channels; j++) {
+ if (coefficient[i * num_channels + j] != 0 ||
+ coefficient[j * num_channels + i] != 0)
+ identity_remix = 0;
+ break;
+ }
+ }
+
+ if (!identity_remix) {
+ msg.fmt_conv = cras_channel_remix_conv_create(num_channels,
+ coefficient);
+ if (NULL == msg.fmt_conv)
+ return -ENOMEM;
+ }
+
+ err = write(thread->to_thread_fds[1], &msg, msg.header.length);
+ if (err < 0) {
+ syslog(LOG_ERR, "Failed to post message to thread.");
+ return err;
+ }
+ /* Synchronous action, wait for response. */
+ err = read(thread->to_main_fds[0], &rsp, sizeof(rsp));
+ if (err < 0) {
+ syslog(LOG_ERR, "Failed to read reply from thread.");
+ return err;
+ }
+
+ if (rsp)
+ cras_fmt_conv_destroy((struct cras_fmt_conv *)rsp);
+ return 0;
+}
+
+struct cras_fmt_conv *audio_thread_get_global_remix_converter()
+{
+ return remix_converter;
+}
+
struct audio_thread *audio_thread_create()
{
int rc;
@@ -1704,12 +1884,11 @@ int audio_thread_add_open_dev(struct audio_thread *thread,
struct audio_thread_open_device_msg msg;
assert(thread && dev);
+
if (!thread->started)
return -EINVAL;
- msg.header.id = AUDIO_THREAD_ADD_OPEN_DEV;
- msg.header.length = sizeof(struct audio_thread_open_device_msg);
- msg.dev = dev;
+ init_open_device_msg(&msg, AUDIO_THREAD_ADD_OPEN_DEV, dev);
return audio_thread_post_message(thread, &msg.header);
}
@@ -1722,9 +1901,23 @@ int audio_thread_rm_open_dev(struct audio_thread *thread,
if (!thread->started)
return -EINVAL;
- msg.header.id = AUDIO_THREAD_RM_OPEN_DEV;
- msg.header.length = sizeof(struct audio_thread_open_device_msg);
- msg.dev = dev;
+ init_open_device_msg(&msg, AUDIO_THREAD_RM_OPEN_DEV, dev);
+ return audio_thread_post_message(thread, &msg.header);
+}
+
+int audio_thread_dev_start_ramp(struct audio_thread *thread,
+ struct cras_iodev *dev,
+ enum CRAS_IODEV_RAMP_REQUEST request)
+{
+ struct audio_thread_dev_start_ramp_msg msg;
+
+ assert(thread && dev);
+
+ if (!thread->started)
+ return -EINVAL;
+
+ init_device_start_ramp_msg(&msg, AUDIO_THREAD_DEV_START_RAMP,
+ dev, request);
return audio_thread_post_message(thread, &msg.header);
}
@@ -1765,5 +1958,8 @@ void audio_thread_destroy(struct audio_thread *thread)
close(thread->to_main_fds[1]);
}
+ if (remix_converter)
+ cras_fmt_conv_destroy(remix_converter);
+
free(thread);
}
diff --git a/cras/src/server/audio_thread.h b/cras/src/server/audio_thread.h
index 2d45daae..1131e0cd 100644
--- a/cras/src/server/audio_thread.h
+++ b/cras/src/server/audio_thread.h
@@ -9,6 +9,7 @@
#include <pthread.h>
#include <stdint.h>
+#include "cras_iodev.h"
#include "cras_types.h"
struct buffer_share;
@@ -104,6 +105,14 @@ void audio_thread_add_write_callback(int fd, thread_callback cb,
*/
void audio_thread_rm_callback(int fd);
+/* Removes a thread_callback from main thread.
+ * Args:
+ * thread - The thread to remove callback from.
+ * fd - The file descriptor of the previous added callback.
+ */
+int audio_thread_rm_callback_sync(struct audio_thread *thread, int fd);
+
+
/* Enables or Disabled the callback associated with fd. */
void audio_thread_enable_callback(int fd, int enabled);
@@ -163,4 +172,29 @@ int audio_thread_disconnect_stream(struct audio_thread *thread,
int audio_thread_dump_thread_info(struct audio_thread *thread,
struct audio_debug_info *info);
+/* Configures the global converter for output remixing. Called by main
+ * thread. */
+int audio_thread_config_global_remix(struct audio_thread *thread,
+ unsigned int num_channels,
+ const float *coefficient);
+
+/* Gets the global remix converter. */
+struct cras_fmt_conv *audio_thread_get_global_remix_converter();
+
+
+/* Start ramping on a device.
+ *
+ * Ramping is started/updated in audio thread. This function lets the main
+ * thread request that the audio thread start ramping.
+ *
+ * Args:
+ * thread - a pointer to the audio thread.
+ * dev - the device to start ramping.
+ * request - Check the docstrings of CRAS_IODEV_RAMP_REQUEST.
+ * Returns:
+ * 0 on success, negative if error.
+ */
+int audio_thread_dev_start_ramp(struct audio_thread *thread,
+ struct cras_iodev *dev,
+ enum CRAS_IODEV_RAMP_REQUEST request);
#endif /* AUDIO_THREAD_H_ */
diff --git a/cras/src/server/config/cras_card_config.c b/cras/src/server/config/cras_card_config.c
index eac01baa..ba39ce94 100644
--- a/cras/src/server/config/cras_card_config.c
+++ b/cras/src/server/config/cras_card_config.c
@@ -49,6 +49,7 @@ static struct cras_volume_curve *create_explicit_curve(
ini_key[MAX_KEY_LEN] = 0;
dB_values[i] = iniparser_getint(card_config->ini, ini_key, 0);
}
+ syslog(LOG_INFO, "Explicit volume curve found for %s.", control_name);
return cras_volume_curve_create_explicit(dB_values);
}
@@ -97,7 +98,7 @@ struct cras_volume_curve *cras_card_config_get_volume_curve_for_control(
const char *curve_type;
if (card_config == NULL || control_name == NULL)
- return cras_volume_curve_create_default();
+ return NULL;
snprintf(ini_key, MAX_KEY_LEN, "%s:volume_curve", control_name);
ini_key[MAX_KEY_LEN] = 0;
@@ -107,6 +108,6 @@ struct cras_volume_curve *cras_card_config_get_volume_curve_for_control(
return create_simple_step_curve(card_config, control_name);
if (curve_type && strcmp(curve_type, "explicit") == 0)
return create_explicit_curve(card_config, control_name);
- syslog(LOG_INFO, "No configure curve found for %s.", control_name);
- return cras_volume_curve_create_default();
+ syslog(LOG_DEBUG, "No configure curve found for %s.", control_name);
+ return NULL;
}
diff --git a/cras/src/server/config/cras_card_config.h b/cras/src/server/config/cras_card_config.h
index 000b8ab9..6ec9ad1c 100644
--- a/cras/src/server/config/cras_card_config.h
+++ b/cras/src/server/config/cras_card_config.h
@@ -29,8 +29,7 @@ void cras_card_config_destroy(struct cras_card_config *card_config);
* Args:
* card_config - Card configuration returned by cras_card_config_create()
* Returns:
- * The specialized curve for the control if there is one, otherwise the
- * default volume curve.
+ * The specialized curve for the control if there is one, otherwise NULL.
*/
struct cras_volume_curve *cras_card_config_get_volume_curve_for_control(
const struct cras_card_config *card_config,
diff --git a/cras/src/server/cras.c b/cras/src/server/cras.c
index 5deaa5a0..29806a81 100644
--- a/cras/src/server/cras.c
+++ b/cras/src/server/cras.c
@@ -17,6 +17,8 @@ static struct option long_options[] = {
{"dsp_config", required_argument, 0, 'd'},
{"syslog_mask", required_argument, 0, 'l'},
{"device_config_dir", required_argument, 0, 'c'},
+ {"disable_profile", required_argument, 0, 'D'},
+ {"internal_ucm_suffix", required_argument, 0, 'u'},
{0, 0, 0, 0}
};
@@ -35,6 +37,8 @@ int main(int argc, char **argv)
const char default_dsp_config[] = CRAS_CONFIG_FILE_DIR "/dsp.ini";
const char *dsp_config = default_dsp_config;
const char *device_config_dir = CRAS_CONFIG_FILE_DIR;
+ const char *internal_ucm_suffix = NULL;
+ unsigned int profile_disable_mask = 0;
set_signals();
@@ -61,6 +65,28 @@ int main(int argc, char **argv)
case 'd':
dsp_config = optarg;
break;
+ /* --disable_profile option takes list of profile names separated by ',' */
+ case 'D':
+ while ((optarg != NULL) && (*optarg != 0)) {
+ if (strncmp(optarg, "hfp", 3) == 0) {
+ profile_disable_mask |= CRAS_SERVER_PROFILE_MASK_HFP;
+ }
+ if (strncmp(optarg, "hsp", 3) == 0) {
+ profile_disable_mask |= CRAS_SERVER_PROFILE_MASK_HSP;
+ }
+ if (strncmp(optarg, "a2dp", 4) == 0) {
+ profile_disable_mask |= CRAS_SERVER_PROFILE_MASK_A2DP;
+ }
+ optarg = strchr(optarg, ',');
+ if (optarg != NULL) {
+ optarg++;
+ }
+ }
+ break;
+ case 'u':
+ if (*optarg != 0)
+ internal_ucm_suffix = optarg;
+ break;
default:
break;
}
@@ -83,11 +109,11 @@ int main(int argc, char **argv)
/* Initialize system. */
cras_server_init();
cras_system_state_init(device_config_dir);
+ if (internal_ucm_suffix)
+ cras_system_state_set_internal_ucm_suffix(internal_ucm_suffix);
cras_dsp_init(dsp_config);
cras_iodev_list_init();
/* Start the server. */
- cras_server_run();
-
- return 0;
+ return cras_server_run(profile_disable_mask);
}
diff --git a/cras/src/server/cras_a2dp_endpoint.c b/cras/src/server/cras_a2dp_endpoint.c
index 6de184d6..3afe8b9f 100644
--- a/cras/src/server/cras_a2dp_endpoint.c
+++ b/cras/src/server/cras_a2dp_endpoint.c
@@ -13,23 +13,12 @@
#include "cras_iodev.h"
#include "cras_bt_constants.h"
#include "cras_bt_endpoint.h"
-#include "cras_hfp_ag_profile.h"
-#include "cras_main_message.h"
#include "cras_system_state.h"
#include "cras_util.h"
#define A2DP_SOURCE_ENDPOINT_PATH "/org/chromium/Cras/Bluetooth/A2DPSource"
#define A2DP_SINK_ENDPOINT_PATH "/org/chromium/Cras/Bluetooth/A2DPSink"
-enum A2DP_COMMAND {
- A2DP_FORCE_SUSPEND,
-};
-
-struct a2dp_msg {
- struct cras_main_message header;
- enum A2DP_COMMAND cmd;
- struct cras_iodev *dev;
-};
/* Pointers for the only connected a2dp device. */
static struct a2dp {
@@ -37,27 +26,6 @@ static struct a2dp {
struct cras_bt_device *device;
} connected_a2dp;
-/*
- * Force suspends a cras_iodev when unexpect error occurs.
- */
-static void cras_a2dp_force_suspend(struct cras_iodev *dev)
-{
- int err;
- struct a2dp_msg msg;
-
- msg.header.type = CRAS_MAIN_A2DP;
- msg.header.length = sizeof(msg);
- msg.cmd = A2DP_FORCE_SUSPEND;
- msg.dev = dev;
-
- err = cras_main_message_send((struct cras_main_message *)&msg);
- if (err < 0) {
- syslog(LOG_ERR, "Failed to post message to main thread");
- return;
- }
- return;
-}
-
static int cras_a2dp_get_capabilities(struct cras_bt_endpoint *endpoint,
void *capabilities, int *len)
{
@@ -163,61 +131,20 @@ static int cras_a2dp_select_configuration(struct cras_bt_endpoint *endpoint,
return 0;
}
-static void cras_a2dp_start(struct cras_bt_endpoint *endpoint,
+static void cras_a2dp_set_configuration(struct cras_bt_endpoint *endpoint,
struct cras_bt_transport *transport)
{
- syslog(LOG_INFO, "Creating iodev for A2DP device");
-
- if (connected_a2dp.iodev) {
- syslog(LOG_WARNING,
- "Replacing existing endpoint configuration");
- a2dp_iodev_destroy(connected_a2dp.iodev);
- }
-
- /* When A2DP-only device connected, suspend all HFP/HSP audio
- * gateways. */
- if (!cras_bt_device_supports_profile(
- cras_bt_transport_device(transport),
- CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE |
- CRAS_BT_DEVICE_PROFILE_HSP_HEADSET))
- cras_hfp_ag_suspend();
-
-
- connected_a2dp.iodev = a2dp_iodev_create(transport,
- cras_a2dp_force_suspend);
- connected_a2dp.device = cras_bt_transport_device(transport);
+ struct cras_bt_device *device;
- if (!connected_a2dp.iodev)
- syslog(LOG_WARNING, "Failed to create a2dp iodev");
+ device = cras_bt_transport_device(transport);
+ cras_bt_device_a2dp_configured(device);
}
static void cras_a2dp_suspend(struct cras_bt_endpoint *endpoint,
struct cras_bt_transport *transport)
{
- cras_a2dp_suspend_connected_device();
-}
-
-/* Handles a2dp messages in main thread.
- */
-static void a2dp_handle_message(struct cras_main_message *msg, void *arg)
-{
- struct a2dp_msg *a2dp_msg = (struct a2dp_msg *)msg;
-
- switch (a2dp_msg->cmd) {
- case A2DP_FORCE_SUSPEND:
- /* If the iodev to force suspend no longer active,
- * ignore the message. */
- if (connected_a2dp.iodev != a2dp_msg->dev)
- break;
- a2dp_iodev_destroy(connected_a2dp.iodev);
- connected_a2dp.iodev = NULL;
- connected_a2dp.device = NULL;
- break;
- default:
- syslog(LOG_ERR, "Unhandled a2dp command");
- break;
- }
- return;
+ struct cras_bt_device *device = cras_bt_transport_device(transport);
+ cras_a2dp_suspend_connected_device(device);
}
static void a2dp_transport_state_changed(struct cras_bt_endpoint *endpoint,
@@ -244,29 +171,52 @@ static struct cras_bt_endpoint cras_a2dp_endpoint = {
.get_capabilities = cras_a2dp_get_capabilities,
.select_configuration = cras_a2dp_select_configuration,
- .start = cras_a2dp_start,
+ .set_configuration = cras_a2dp_set_configuration,
.suspend = cras_a2dp_suspend,
.transport_state_changed = a2dp_transport_state_changed
};
int cras_a2dp_endpoint_create(DBusConnection *conn)
{
- cras_main_message_add_handler(CRAS_MAIN_A2DP,
- a2dp_handle_message, NULL);
return cras_bt_endpoint_add(conn, &cras_a2dp_endpoint);
}
+void cras_a2dp_start(struct cras_bt_device *device)
+{
+ struct cras_bt_transport *transport = cras_a2dp_endpoint.transport;
+
+ if (!transport || device != cras_bt_transport_device(transport)) {
+ syslog(LOG_ERR, "Device and active transport not match.");
+ return;
+ }
+
+ if (connected_a2dp.iodev) {
+ syslog(LOG_WARNING,
+ "Replacing existing endpoint configuration");
+ a2dp_iodev_destroy(connected_a2dp.iodev);
+ }
+
+ connected_a2dp.iodev = a2dp_iodev_create(transport);
+ connected_a2dp.device = cras_bt_transport_device(transport);
+
+ if (!connected_a2dp.iodev)
+ syslog(LOG_WARNING, "Failed to create a2dp iodev");
+}
+
struct cras_bt_device *cras_a2dp_connected_device()
{
return connected_a2dp.device;
}
-void cras_a2dp_suspend_connected_device()
+void cras_a2dp_suspend_connected_device(struct cras_bt_device *device)
{
+ if (connected_a2dp.device != device)
+ return;
+
if (connected_a2dp.iodev) {
syslog(LOG_INFO, "Destroying iodev for A2DP device");
a2dp_iodev_destroy(connected_a2dp.iodev);
connected_a2dp.iodev = NULL;
connected_a2dp.device = NULL;
}
-} \ No newline at end of file
+}
diff --git a/cras/src/server/cras_a2dp_endpoint.h b/cras/src/server/cras_a2dp_endpoint.h
index 397ff4b7..1ebd00d6 100644
--- a/cras/src/server/cras_a2dp_endpoint.h
+++ b/cras/src/server/cras_a2dp_endpoint.h
@@ -8,6 +8,8 @@
#include <dbus/dbus.h>
+struct cras_iodev;
+
int cras_a2dp_endpoint_create(DBusConnection *conn);
/* Gets the connected a2dp device, NULL is returned when there's none. */
@@ -16,6 +18,9 @@ struct cras_bt_device *cras_a2dp_connected_device();
/* Suspends the connected a2dp device, the purpose is to remove a2dp iodev
* to release a2dp audio before sending dbus message to disconnect a2dp
* device. */
-void cras_a2dp_suspend_connected_device();
+void cras_a2dp_suspend_connected_device(struct cras_bt_device *device);
+
+/* Starts A2DP output by creating the cras_iodev. */
+void cras_a2dp_start(struct cras_bt_device *device);
#endif /* CRAS_A2DP_ENDPOINT_H_ */
diff --git a/cras/src/server/cras_a2dp_info.c b/cras/src/server/cras_a2dp_info.c
index 18a89454..80a498c4 100644
--- a/cras/src/server/cras_a2dp_info.c
+++ b/cras/src/server/cras_a2dp_info.c
@@ -115,7 +115,7 @@ void a2dp_drain(struct a2dp_info *a2dp)
static int avdtp_write(int stream_fd, struct a2dp_info *a2dp)
{
- int err;
+ int err, samples;
struct rtp_header *header;
struct rtp_payload *payload;
@@ -135,13 +135,16 @@ static int avdtp_write(int stream_fd, struct a2dp_info *a2dp)
if (err < 0)
return -errno;
+ /* Returns the number of samples in frame. */
+ samples = a2dp->samples;
+
/* Reset some data */
a2dp->a2dp_buf_used = sizeof(*header) + sizeof(*payload);
a2dp->frame_count = 0;
a2dp->samples = 0;
a2dp->seq_num++;
- return err;
+ return samples;
}
int a2dp_encode(struct a2dp_info *a2dp, const void *pcm_buf, int pcm_buf_size,
diff --git a/cras/src/server/cras_a2dp_info.h b/cras/src/server/cras_a2dp_info.h
index 9bb5ce30..eaf00d5b 100644
--- a/cras/src/server/cras_a2dp_info.h
+++ b/cras/src/server/cras_a2dp_info.h
@@ -78,7 +78,7 @@ int a2dp_encode(struct a2dp_info *a2dp, const void *pcm_buf, int pcm_buf_size,
int format_bytes, size_t link_mtu);
/*
- * Writes samples using a2dp, returns numbewr of bytes written.
+ * Writes samples using a2dp, returns number of frames written.
* Args:
* a2dp: The a2dp info object.
* stream_fd: The file descriptor to send stream to.
diff --git a/cras/src/server/cras_a2dp_iodev.c b/cras/src/server/cras_a2dp_iodev.c
index a4450ffd..a9551a12 100644
--- a/cras/src/server/cras_a2dp_iodev.c
+++ b/cras/src/server/cras_a2dp_iodev.c
@@ -14,6 +14,8 @@
#include "audio_thread.h"
#include "audio_thread_log.h"
#include "byte_buffer.h"
+#include "cras_iodev_list.h"
+#include "cras_a2dp_endpoint.h"
#include "cras_a2dp_info.h"
#include "cras_a2dp_iodev.h"
#include "cras_audio_area.h"
@@ -27,23 +29,28 @@
#define PCM_BUF_MAX_SIZE_FRAMES (4096*4)
#define PCM_BUF_MAX_SIZE_BYTES (PCM_BUF_MAX_SIZE_FRAMES * 4)
+/* Child of cras_iodev to handle bluetooth A2DP streaming.
+ * Members:
+ * base - The cras_iodev structure "base class"
+ * a2dp - The codec and encoded state of a2dp_io.
+ * transport - The transport object for bluez media API.
+ * sock_depth_frames - Socket depth in frames of the a2dp socket.
+ * pcm_buf - Buffer to hold pcm samples before encode.
+ * destroyed - Flag to note if this a2dp_io is about to destroy.
+ * pre_fill_complete - Flag to note if socket pre-fill is completed.
+ * bt_written_frames - Accumulated frames written to a2dp socket. Used
+ * together with the device open timestamp to estimate how many virtual
+ * buffer is queued there.
+ * dev_open_time - The last time a2dp_ios is opened.
+ */
struct a2dp_io {
struct cras_iodev base;
struct a2dp_info a2dp;
struct cras_bt_transport *transport;
- a2dp_force_suspend_cb force_suspend_cb;
unsigned sock_depth_frames;
-
- /* To hold the pcm samples. */
struct byte_buffer *pcm_buf;
-
- /* Has the socket been filled once. */
+ int destroyed;
int pre_fill_complete;
-
- /* Accumulated frames written to a2dp socket. Will need this info
- * together with the device open time stamp to get how many virtual
- * buffer is queued there.
- */
uint64_t bt_written_frames;
struct timespec dev_open_time;
};
@@ -114,7 +121,8 @@ static int bt_queued_frames(const struct cras_iodev *iodev, int fr)
}
-static int frames_queued(const struct cras_iodev *iodev)
+static int frames_queued(const struct cras_iodev *iodev,
+ struct timespec *tstamp)
{
struct a2dp_io *a2dpio = (struct a2dp_io *)iodev;
int estimate_queued_frames = bt_queued_frames(iodev, 0);
@@ -122,7 +130,7 @@ static int frames_queued(const struct cras_iodev *iodev)
a2dp_queued_frames(&a2dpio->a2dp) +
buf_queued_bytes(a2dpio->pcm_buf) /
cras_get_format_bytes(iodev->format);
-
+ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
return MIN(iodev->buffer_size,
MAX(estimate_queued_frames, local_queued_frames));
}
@@ -139,6 +147,11 @@ static int open_dev(struct cras_iodev *iodev)
return err;
}
+ /* Apply the node's volume after transport is acquired. Doing this
+ * is necessary because the volume can not sync to hardware until
+ * it is opened. */
+ iodev->set_volume(iodev);
+
/* Assert format is set before opening device. */
if (iodev->format == NULL)
return -EINVAL;
@@ -181,16 +194,25 @@ static int close_dev(struct cras_iodev *iodev)
{
int err;
struct a2dp_io *a2dpio = (struct a2dp_io *)iodev;
+ struct cras_bt_device *device;
if (!a2dpio->transport)
return 0;
- audio_thread_rm_callback(cras_bt_transport_fd(a2dpio->transport));
+ /* Remove audio thread callback and sync before releasing
+ * the transport. */
+ audio_thread_rm_callback_sync(
+ cras_iodev_list_get_audio_thread(),
+ cras_bt_transport_fd(a2dpio->transport));
- err = cras_bt_transport_release(a2dpio->transport);
+ err = cras_bt_transport_release(a2dpio->transport,
+ !a2dpio->destroyed);
if (err < 0)
syslog(LOG_ERR, "transport_release failed");
+ device = cras_bt_transport_device(a2dpio->transport);
+ if (device)
+ cras_bt_device_cancel_suspend(device);
a2dp_drain(&a2dpio->a2dp);
byte_buffer_destroy(a2dpio->pcm_buf);
cras_iodev_free_format(iodev);
@@ -198,12 +220,6 @@ static int close_dev(struct cras_iodev *iodev)
return 0;
}
-static int is_open(const struct cras_iodev *iodev)
-{
- struct a2dp_io *a2dpio = (struct a2dp_io *)iodev;
- return cras_bt_transport_fd(a2dpio->transport) > 0;
-}
-
static int pre_fill_socket(struct a2dp_io *a2dpio)
{
static const uint16_t zero_buffer[1024 * 2];
@@ -245,15 +261,22 @@ static int pre_fill_socket(struct a2dp_io *a2dpio)
*/
static int flush_data(void *arg)
{
- const struct cras_iodev *iodev = (const struct cras_iodev *)arg;
+ struct cras_iodev *iodev = (struct cras_iodev *)arg;
int processed;
size_t format_bytes;
- int err = 0;
int written = 0;
+ int queued_frames;
struct a2dp_io *a2dpio;
+ struct cras_bt_device *device;
a2dpio = (struct a2dp_io *)iodev;
format_bytes = cras_get_format_bytes(iodev->format);
+ device = cras_bt_transport_device(a2dpio->transport);
+
+ /* If bt device has been destroyed, this a2dp iodev will soon be
+ * destroyed as well. */
+ if (device == NULL)
+ return -EINVAL;
encode_more:
while (buf_queued_bytes(a2dpio->pcm_buf)) {
@@ -283,40 +306,46 @@ encode_more:
written,
a2dp_queued_frames(&a2dpio->a2dp), 0);
if (written == -EAGAIN) {
+ /* If EAGAIN error lasts longer than 5 seconds, suspend the
+ * a2dp connection. */
+ cras_bt_device_schedule_suspend(device, 5000);
audio_thread_enable_callback(
cras_bt_transport_fd(a2dpio->transport), 1);
return 0;
} else if (written < 0) {
- if (a2dpio->force_suspend_cb)
- a2dpio->force_suspend_cb(&a2dpio->base);
- err = written;
- goto write_done;
- } else if (written == 0) {
- goto write_done;
+ /* Suspend a2dp immediately when receives error other than
+ * EAGAIN. */
+ cras_bt_device_cancel_suspend(device);
+ cras_bt_device_schedule_suspend(device, 0);
+ return written;
}
- if (buf_queued_bytes(a2dpio->pcm_buf))
+ /* Data succcessfully written to a2dp socket, cancel any scheduled
+ * suspend timer. */
+ cras_bt_device_cancel_suspend(device);
+
+ /* If it looks okay to write more and we do have queued data, try
+ * encode more. But avoid the case when PCM buffer level is too close
+ * to min_buffer_level so that another A2DP write could causes underrun.
+ */
+ queued_frames = buf_queued_bytes(a2dpio->pcm_buf) / format_bytes;
+ if (written && (iodev->min_buffer_level + written < queued_frames))
goto encode_more;
-write_done:
/* everything written. */
audio_thread_enable_callback(
cras_bt_transport_fd(a2dpio->transport), 0);
- return err;
-}
-
-static int dev_running(const struct cras_iodev *iodev)
-{
- return is_open(iodev);
+ return 0;
}
static int delay_frames(const struct cras_iodev *iodev)
{
const struct a2dp_io *a2dpio = (struct a2dp_io *)iodev;
+ struct timespec tstamp;
/* The number of frames in the pcm buffer plus two mtu packets */
- return frames_queued(iodev) + a2dpio->sock_depth_frames;
+ return frames_queued(iodev, &tstamp) + a2dpio->sock_depth_frames;
}
static int get_buffer(struct cras_iodev *iodev,
@@ -376,7 +405,24 @@ static int flush_buffer(struct cras_iodev *iodev)
return 0;
}
-static void update_active_node(struct cras_iodev *iodev, unsigned node_idx)
+static void set_volume(struct cras_iodev *iodev)
+{
+ size_t volume;
+ struct a2dp_io *a2dpio = (struct a2dp_io *)iodev;
+ struct cras_bt_device *device =
+ cras_bt_transport_device(a2dpio->transport);
+
+ if (!cras_bt_device_get_use_hardware_volume(device))
+ return;
+
+ volume = iodev->active_node->volume * 127 / 100;
+
+ if (a2dpio->transport)
+ cras_bt_transport_set_volume(a2dpio->transport, volume);
+}
+
+static void update_active_node(struct cras_iodev *iodev, unsigned node_idx,
+ unsigned dev_enabled)
{
}
@@ -394,8 +440,7 @@ void free_resources(struct a2dp_io *a2dpio)
destroy_a2dp(&a2dpio->a2dp);
}
-struct cras_iodev *a2dp_iodev_create(struct cras_bt_transport *transport,
- a2dp_force_suspend_cb force_suspend_cb)
+struct cras_iodev *a2dp_iodev_create(struct cras_bt_transport *transport)
{
int err;
struct a2dp_io *a2dpio;
@@ -417,7 +462,6 @@ struct cras_iodev *a2dp_iodev_create(struct cras_bt_transport *transport,
syslog(LOG_ERR, "Fail to init a2dp");
goto error;
}
- a2dpio->force_suspend_cb = force_suspend_cb;
iodev = &a2dpio->base;
@@ -438,11 +482,10 @@ struct cras_iodev *a2dp_iodev_create(struct cras_bt_transport *transport,
cras_bt_device_object_path(device),
strlen(cras_bt_device_object_path(device)),
strlen(cras_bt_device_object_path(device)));
+ iodev->info.stable_id_new = iodev->info.stable_id;
iodev->open_dev = open_dev;
- iodev->is_open = is_open; /* Needed by thread_add_stream */
iodev->frames_queued = frames_queued;
- iodev->dev_running = dev_running;
iodev->delay_frames = delay_frames;
iodev->get_buffer = get_buffer;
iodev->put_buffer = put_buffer;
@@ -450,7 +493,7 @@ struct cras_iodev *a2dp_iodev_create(struct cras_bt_transport *transport,
iodev->close_dev = close_dev;
iodev->update_supported_formats = update_supported_formats;
iodev->update_active_node = update_active_node;
- iodev->software_volume_needed = 1;
+ iodev->set_volume = set_volume;
/* Create a dummy ionode */
node = (struct cras_ionode *)calloc(1, sizeof(*node));
@@ -481,6 +524,7 @@ void a2dp_iodev_destroy(struct cras_iodev *iodev)
struct a2dp_io *a2dpio = (struct a2dp_io *)iodev;
struct cras_bt_device *device;
+ a2dpio->destroyed = 1;
device = cras_bt_transport_device(a2dpio->transport);
/* A2DP does output only */
diff --git a/cras/src/server/cras_a2dp_iodev.h b/cras/src/server/cras_a2dp_iodev.h
index 0055c9c8..e33af6c8 100644
--- a/cras/src/server/cras_a2dp_iodev.h
+++ b/cras/src/server/cras_a2dp_iodev.h
@@ -10,20 +10,13 @@
struct cras_iodev;
-/* Callback to force suspend a a2dp iodev. */
-typedef void (*a2dp_force_suspend_cb)(struct cras_iodev *iodev);
-
/*
* Creates an a2dp iodev from transport object.
* Args:
* transport - The transport to create a2dp iodev for
- * force_suspend_cb - The callback to trigger when severe error occurs
- * during transmitting audio, used to force suspend an a2dp iodev
- * outside the life cycle controlled by bluetooth daemon.
*/
struct cras_iodev *a2dp_iodev_create(
- struct cras_bt_transport *transport,
- a2dp_force_suspend_cb force_suspend_cb);
+ struct cras_bt_transport *transport);
/*
* Destroys a2dp iodev.
diff --git a/cras/src/server/cras_alert.c b/cras/src/server/cras_alert.c
index dbea44df..6c97592b 100644
--- a/cras/src/server/cras_alert.c
+++ b/cras/src/server/cras_alert.c
@@ -4,7 +4,9 @@
*/
#include <errno.h>
+#include <stddef.h>
#include <stdlib.h>
+#include <string.h>
#include "cras_alert.h"
#include "utlist.h"
@@ -16,10 +18,19 @@ struct cras_alert_cb_list {
struct cras_alert_cb_list *prev, *next;
};
+/* A list of data args to callbacks. Variable-length structure. */
+struct cras_alert_data {
+ struct cras_alert_data *prev, *next;
+ /* This field must be the last in this structure. */
+ char buf[];
+};
+
struct cras_alert {
int pending;
+ unsigned int flags;
cras_alert_prepare prepare;
struct cras_alert_cb_list *callbacks;
+ struct cras_alert_data *data;
struct cras_alert *prev, *next;
};
@@ -28,13 +39,15 @@ static struct cras_alert *all_alerts;
/* If there is any alert pending. */
static int has_alert_pending;
-struct cras_alert *cras_alert_create(cras_alert_prepare prepare)
+struct cras_alert *cras_alert_create(cras_alert_prepare prepare,
+ unsigned int flags)
{
struct cras_alert *alert;
alert = calloc(1, sizeof(*alert));
if (!alert)
return NULL;
alert->prepare = prepare;
+ alert->flags = flags;
DL_APPEND(all_alerts, alert);
return alert;
}
@@ -79,13 +92,26 @@ int cras_alert_rm_callback(struct cras_alert *alert, cras_alert_cb cb,
static void cras_alert_process(struct cras_alert *alert)
{
struct cras_alert_cb_list *cb;
+ struct cras_alert_data *data;
+
+ if (!alert->pending)
+ return;
+
+ alert->pending = 0;
+ if (alert->prepare)
+ alert->prepare(alert);
- if (alert->pending) {
- alert->pending = 0;
- if (alert->prepare)
- alert->prepare(alert);
+ if (!alert->data) {
DL_FOREACH(alert->callbacks, cb)
- cb->callback(cb->arg);
+ cb->callback(cb->arg, NULL);
+ }
+
+ /* Have data arguments, pass each to the callbacks. */
+ DL_FOREACH(alert->data, data) {
+ DL_FOREACH(alert->callbacks, cb)
+ cb->callback(cb->arg, (void *)data->buf);
+ DL_DELETE(alert->data, data);
+ free(data);
}
}
@@ -95,6 +121,27 @@ void cras_alert_pending(struct cras_alert *alert)
has_alert_pending = 1;
}
+void cras_alert_pending_data(struct cras_alert *alert,
+ void *data, size_t data_size)
+{
+ struct cras_alert_data *d;
+
+ alert->pending = 1;
+ has_alert_pending = 1;
+ d = calloc(1, offsetof(struct cras_alert_data, buf) + data_size);
+ memcpy(d->buf, data, data_size);
+
+ if (!(alert->flags & CRAS_ALERT_FLAG_KEEP_ALL_DATA) && alert->data) {
+ /* There will never be more than one item in the list. */
+ free(alert->data);
+ alert->data = NULL;
+ }
+
+ /* Even when there is only one item, it is important to use DL_APPEND
+ * here so that d's next and prev pointers are setup correctly. */
+ DL_APPEND(alert->data, d);
+}
+
void cras_alert_process_all_pending_alerts()
{
struct cras_alert *alert;
@@ -109,6 +156,7 @@ void cras_alert_process_all_pending_alerts()
void cras_alert_destroy(struct cras_alert *alert)
{
struct cras_alert_cb_list *cb;
+ struct cras_alert_data *data;
if (!alert)
return;
@@ -118,6 +166,11 @@ void cras_alert_destroy(struct cras_alert *alert)
free(cb);
}
+ DL_FOREACH(alert->data, data) {
+ DL_DELETE(alert->data, data);
+ free(data);
+ }
+
alert->callbacks = NULL;
DL_DELETE(all_alerts, alert);
free(alert);
diff --git a/cras/src/server/cras_alert.h b/cras/src/server/cras_alert.h
index ffdf1437..3fee90b1 100644
--- a/cras/src/server/cras_alert.h
+++ b/cras/src/server/cras_alert.h
@@ -32,19 +32,33 @@ extern "C" {
struct cras_alert;
/* Callback functions to be notified when settings change. arg is a user
- * provided argument that will be passed back. */
-typedef void (*cras_alert_cb)(void *arg);
+ * provided argument that will be passed back, data is extra info about the
+ * signal if available.
+ */
+typedef void (*cras_alert_cb)(void *arg, void *data);
typedef void (*cras_alert_prepare)(struct cras_alert *alert);
+/* Flags for alerts. */
+enum CRAS_ALERT_FLAGS {
+ /* By default, alerts will only keep the last copy of the data
+ * specified in cras_alert_pending_data as an optimization - then
+ * the callback is executed once with the latest value, rather than
+ * for every value. In some cases, it is important to send the data
+ * with every change. Use this flag to enable that behavior. */
+ CRAS_ALERT_FLAG_KEEP_ALL_DATA = 1 << 0,
+};
+
/* Creates an alert.
* Args:
* prepare - A function which will be called before calling the callbacks.
* The prepare function should update the system state in the shared
* memory to be consistent. It can be NULL if not needed.
+ * flags - 0 for defauts, or ORed values from enum CRAS_ALERT_FLAGS.
* Returns:
* A pointer to the alert, NULL if out of memory.
*/
-struct cras_alert *cras_alert_create(cras_alert_prepare prepare);
+struct cras_alert *cras_alert_create(cras_alert_prepare prepare,
+ unsigned int flags);
/* Adds a callback to the alert.
* Args:
@@ -77,6 +91,20 @@ int cras_alert_rm_callback(struct cras_alert *alert, cras_alert_cb cb,
*/
void cras_alert_pending(struct cras_alert *alert);
+/* Marks an alert as pending. We don't call the callbacks immediately when an
+ * alert becomes pending, but will do that when
+ * cras_alert_process_all_pending_alerts() is called.
+ * By default only the last data value supplied here is provided as an
+ * argument to the callback. To have the callback executed with every
+ * data value, call cras_alert_keep_all_data() (see above).
+ * Args:
+ * alert - A pointer to the alert.
+ * data - A pointer to data that is copied and passed to the callback.
+ * data_size - Size of the data.
+ */
+void cras_alert_pending_data(struct cras_alert *alert,
+ void *data, size_t data_size);
+
/* Processes all alerts that are pending.
*
* For all pending alerts, its prepare function will be called, then the
diff --git a/cras/src/server/cras_alsa_card.c b/cras/src/server/cras_alsa_card.c
index 080a719c..16a6ef5a 100644
--- a/cras/src/server/cras_alsa_card.c
+++ b/cras/src/server/cras_alsa_card.c
@@ -3,8 +3,11 @@
* found in the LICENSE file.
*/
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* For asprintf */
+#endif
+
#include <alsa/asoundlib.h>
-#include <alsa/use-case.h>
#include <syslog.h>
#include "cras_alsa_card.h"
@@ -16,6 +19,7 @@
#include "cras_config.h"
#include "cras_iodev.h"
#include "cras_iodev_list.h"
+#include "cras_system_state.h"
#include "cras_types.h"
#include "cras_util.h"
#include "utlist.h"
@@ -23,6 +27,7 @@
#define MAX_ALSA_CARDS 32 /* Alsa limit on number of cards. */
#define MAX_ALSA_PCM_NAME_LENGTH 6 /* Alsa names "hw:XX" + 1 for null. */
#define MAX_INI_NAME_LENGTH 63 /* 63 chars + 1 for null where declared. */
+#define MAX_COUPLED_OUTPUT_SIZE 4
struct iodev_list_node {
struct cras_iodev *iodev;
@@ -30,12 +35,21 @@ struct iodev_list_node {
struct iodev_list_node *prev, *next;
};
+/* Keeps an fd that is registered with system state. A list of fds must be
+ * kept so that they can be removed when the card is destroyed. */
+struct hctl_poll_fd {
+ int fd;
+ struct hctl_poll_fd *prev, *next;
+};
+
/* Holds information about each sound card on the system.
* name - of the form hw:XX,YY.
* card_index - 0 based index, value of "XX" in the name.
* iodevs - Input and output devices for this card.
* mixer - Controls the mixer controls for this card.
- * ucm - ALSA use case manager if available.
+ * ucm - CRAS use case manager if available.
+ * hctl - ALSA high-level control interface.
+ * hctl_poll_fds - List of fds registered with cras_system_state.
* config - Config info for this card, can be NULL if none found.
*/
struct cras_alsa_card {
@@ -43,23 +57,12 @@ struct cras_alsa_card {
size_t card_index;
struct iodev_list_node *iodevs;
struct cras_alsa_mixer *mixer;
- snd_use_case_mgr_t *ucm;
+ struct cras_use_case_mgr *ucm;
+ snd_hctl_t *hctl;
+ struct hctl_poll_fd *hctl_poll_fds;
struct cras_card_config *config;
};
-/* Checks if there are any devices with direction already in the card.
- */
-int is_first_dev(struct cras_alsa_card *alsa_card,
- enum CRAS_STREAM_DIRECTION direction)
-{
- struct iodev_list_node *node;
-
- DL_FOREACH(alsa_card->iodevs, node)
- if (node->direction == direction)
- return 0;
- return 1;
-}
-
/* Creates an iodev for the given device.
* Args:
* alsa_card - the alsa_card the device will be added to.
@@ -69,23 +72,40 @@ int is_first_dev(struct cras_alsa_card *alsa_card,
* dev_id - The id string of the device.
* device_index - 0 based index, value of "YY" in "hw:XX,YY".
* direction - Input or output.
+ * Returns:
+ * Pointer to the created iodev, or NULL on error.
+ * other negative error code otherwise.
*/
-void create_iodev_for_device(struct cras_alsa_card *alsa_card,
- struct cras_alsa_card_info *info,
- const char *card_name,
- const char *dev_name,
- const char *dev_id,
- unsigned device_index,
- enum CRAS_STREAM_DIRECTION direction)
+struct cras_iodev *create_iodev_for_device(
+ struct cras_alsa_card *alsa_card,
+ struct cras_alsa_card_info *info,
+ const char *card_name,
+ const char *dev_name,
+ const char *dev_id,
+ unsigned device_index,
+ enum CRAS_STREAM_DIRECTION direction)
{
struct iodev_list_node *new_dev;
- int first;
-
- first = is_first_dev(alsa_card, direction);
+ struct iodev_list_node *node;
+ int first = 1;
+
+ /* Find whether this is the first device in this direction, and
+ * avoid duplicate device indexes. */
+ DL_FOREACH(alsa_card->iodevs, node) {
+ if (node->direction != direction)
+ continue;
+ first = 0;
+ if (alsa_iodev_index(node->iodev) == device_index) {
+ syslog(LOG_DEBUG,
+ "Skipping duplicate device for %s:%s:%s [%u]",
+ card_name, dev_name, dev_id, device_index);
+ return node->iodev;
+ }
+ }
new_dev = calloc(1, sizeof(*new_dev));
if (new_dev == NULL)
- return;
+ return NULL;
new_dev->direction = direction;
new_dev->iodev = alsa_iodev_create(info->card_index,
@@ -96,15 +116,18 @@ void create_iodev_for_device(struct cras_alsa_card *alsa_card,
info->card_type,
first,
alsa_card->mixer,
+ alsa_card->config,
alsa_card->ucm,
+ alsa_card->hctl,
direction,
info->usb_vendor_id,
- info->usb_product_id);
+ info->usb_product_id,
+ info->usb_serial_number);
if (new_dev->iodev == NULL) {
syslog(LOG_ERR, "Couldn't create alsa_iodev for %u:%u\n",
info->card_index, device_index);
free(new_dev);
- return;
+ return NULL;
}
syslog(LOG_DEBUG, "New %s device %u:%d",
@@ -113,6 +136,21 @@ void create_iodev_for_device(struct cras_alsa_card *alsa_card,
device_index);
DL_APPEND(alsa_card->iodevs, new_dev);
+ return new_dev->iodev;
+}
+
+/* Returns non-zero if this card has hctl jacks.
+ */
+static int card_has_hctl_jack(struct cras_alsa_card *alsa_card)
+{
+ struct iodev_list_node *node;
+
+ /* Find the first device that has an hctl jack. */
+ DL_FOREACH(alsa_card->iodevs, node) {
+ if (alsa_iodev_has_hctl_jacks(node->iodev))
+ return 1;
+ }
+ return 0;
}
/* Check if a device should be ignored for this card. Returns non-zero if the
@@ -132,20 +170,244 @@ static int should_ignore_dev(struct cras_alsa_card_info *info,
}
/* Filters an array of mixer control names. Keep a name if it is
- * specified in the ucm config, otherwise set it to NULL */
-static void filter_mixer_names(snd_use_case_mgr_t *ucm,
- const char *names[],
- size_t names_len)
+ * specified in the ucm config. */
+static struct mixer_name *filter_controls(struct cras_use_case_mgr *ucm,
+ struct mixer_name *controls)
{
- size_t i;
- for (i = 0; i < names_len; i++) {
- char *dev = ucm_get_dev_for_mixer(ucm, names[i],
+ struct mixer_name *control;
+ DL_FOREACH(controls, control) {
+ char *dev = ucm_get_dev_for_mixer(ucm, control->name,
CRAS_STREAM_OUTPUT);
- if (dev)
- free(dev);
- else
- names[i] = NULL;
+ if (!dev)
+ DL_DELETE(controls, control);
+ }
+ return controls;
+}
+
+/* Handles notifications from alsa controls. Called by main thread when a poll
+ * fd provided by alsa signals there is an event available. */
+static void alsa_control_event_pending(void *arg)
+{
+ struct cras_alsa_card *card;
+
+ card = (struct cras_alsa_card *)arg;
+ if (card == NULL) {
+ syslog(LOG_ERR, "Invalid card from control event.");
+ return;
+ }
+
+ /* handle_events will trigger the callback registered with each control
+ * that has changed. */
+ snd_hctl_handle_events(card->hctl);
+}
+
+static int add_controls_and_iodevs_by_matching(
+ struct cras_alsa_card_info *info,
+ struct cras_device_blacklist *blacklist,
+ struct cras_alsa_card *alsa_card,
+ const char *card_name,
+ snd_ctl_t *handle)
+{
+ struct mixer_name *coupled_controls = NULL;
+ int dev_idx;
+ snd_pcm_info_t *dev_info;
+ struct mixer_name *extra_controls = NULL;
+ int rc = 0;
+
+ snd_pcm_info_alloca(&dev_info);
+
+ if (alsa_card->ucm) {
+ char *extra_main_volume;
+
+ /* Filter the extra output mixer names */
+ extra_controls =
+ filter_controls(alsa_card->ucm,
+ mixer_name_add(extra_controls, "IEC958",
+ CRAS_STREAM_OUTPUT,
+ MIXER_NAME_VOLUME));
+
+ /* Get the extra main volume control. */
+ extra_main_volume = ucm_get_flag(alsa_card->ucm,
+ "ExtraMainVolume");
+ if (extra_main_volume) {
+ extra_controls =
+ mixer_name_add(extra_controls,
+ extra_main_volume,
+ CRAS_STREAM_OUTPUT,
+ MIXER_NAME_MAIN_VOLUME);
+ free(extra_main_volume);
+ }
+ mixer_name_dump(extra_controls, "extra controls");
+
+ /* Check if coupled controls has been specified for speaker. */
+ coupled_controls = ucm_get_coupled_mixer_names(
+ alsa_card->ucm, "Speaker");
+ mixer_name_dump(coupled_controls, "coupled controls");
}
+
+ /* Add controls to mixer by name matching. */
+ rc = cras_alsa_mixer_add_controls_by_name_matching(
+ alsa_card->mixer,
+ extra_controls,
+ coupled_controls);
+ if (rc) {
+ syslog(LOG_ERR, "Fail adding controls to mixer for %s.",
+ alsa_card->name);
+ goto error;
+ }
+
+ /* Go through every device. */
+ dev_idx = -1;
+ while (1) {
+ rc = snd_ctl_pcm_next_device(handle, &dev_idx);
+ if (rc < 0)
+ goto error;
+ if (dev_idx < 0)
+ break;
+
+ snd_pcm_info_set_device(dev_info, dev_idx);
+ snd_pcm_info_set_subdevice(dev_info, 0);
+
+ /* Check for playback devices. */
+ snd_pcm_info_set_stream(
+ dev_info, SND_PCM_STREAM_PLAYBACK);
+ if (snd_ctl_pcm_info(handle, dev_info) == 0 &&
+ !should_ignore_dev(info, blacklist, dev_idx)) {
+ struct cras_iodev *iodev =
+ create_iodev_for_device(
+ alsa_card,
+ info,
+ card_name,
+ snd_pcm_info_get_name(dev_info),
+ snd_pcm_info_get_id(dev_info),
+ dev_idx,
+ CRAS_STREAM_OUTPUT);
+ if (iodev) {
+ rc = alsa_iodev_legacy_complete_init(
+ iodev);
+ if (rc < 0)
+ goto error;
+ }
+ }
+
+ /* Check for capture devices. */
+ snd_pcm_info_set_stream(
+ dev_info, SND_PCM_STREAM_CAPTURE);
+ if (snd_ctl_pcm_info(handle, dev_info) == 0) {
+ struct cras_iodev *iodev =
+ create_iodev_for_device(
+ alsa_card,
+ info,
+ card_name,
+ snd_pcm_info_get_name(dev_info),
+ snd_pcm_info_get_id(dev_info),
+ dev_idx,
+ CRAS_STREAM_INPUT);
+ if (iodev) {
+ rc = alsa_iodev_legacy_complete_init(
+ iodev);
+ if (rc < 0)
+ goto error;
+ }
+ }
+ }
+error:
+ mixer_name_free(coupled_controls);
+ mixer_name_free(extra_controls);
+ return rc;
+}
+
+static int add_controls_and_iodevs_with_ucm(
+ struct cras_alsa_card_info *info,
+ struct cras_alsa_card *alsa_card,
+ const char *card_name,
+ snd_ctl_t *handle)
+{
+ snd_pcm_info_t *dev_info;
+ struct iodev_list_node *node;
+ int rc = 0;
+ struct ucm_section *section;
+ struct ucm_section *ucm_sections;
+
+ snd_pcm_info_alloca(&dev_info);
+
+ /* Get info on the devices specified in the UCM config. */
+ ucm_sections = ucm_get_sections(alsa_card->ucm);
+ if (!ucm_sections) {
+ syslog(LOG_ERR,
+ "Could not retrieve any UCM SectionDevice"
+ " info for '%s'.", card_name);
+ rc = -ENOENT;
+ goto error;
+ }
+
+ /* Create all of the controls first. */
+ DL_FOREACH(ucm_sections, section) {
+ rc = cras_alsa_mixer_add_controls_in_section(
+ alsa_card->mixer, section);
+ if (rc) {
+ syslog(LOG_ERR, "Failed adding controls to"
+ " mixer for '%s:%s'",
+ card_name,
+ section->name);
+ goto error;
+ }
+ }
+
+ /* Create all of the devices. */
+ DL_FOREACH(ucm_sections, section) {
+ snd_pcm_info_set_device(dev_info, section->dev_idx);
+ snd_pcm_info_set_subdevice(dev_info, 0);
+ if (section->dir == CRAS_STREAM_OUTPUT)
+ snd_pcm_info_set_stream(
+ dev_info, SND_PCM_STREAM_PLAYBACK);
+ else if (section->dir == CRAS_STREAM_INPUT)
+ snd_pcm_info_set_stream(
+ dev_info, SND_PCM_STREAM_CAPTURE);
+ else {
+ syslog(LOG_ERR, "Unexpected direction: %d",
+ section->dir);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (snd_ctl_pcm_info(handle, dev_info)) {
+ syslog(LOG_ERR,
+ "Could not get info for device: %s",
+ section->name);
+ continue;
+ }
+
+ create_iodev_for_device(
+ alsa_card, info, card_name,
+ snd_pcm_info_get_name(dev_info),
+ snd_pcm_info_get_id(dev_info),
+ section->dev_idx, section->dir);
+ }
+
+ /* Setup jacks and controls for the devices. */
+ DL_FOREACH(ucm_sections, section) {
+ DL_FOREACH(alsa_card->iodevs, node) {
+ if (node->direction == section->dir &&
+ alsa_iodev_index(node->iodev) ==
+ section->dev_idx)
+ break;
+ }
+ if (node) {
+ rc = alsa_iodev_ucm_add_nodes_and_jacks(
+ node->iodev, section);
+ if (rc < 0)
+ goto error;
+ }
+ }
+
+ DL_FOREACH(alsa_card->iodevs, node) {
+ alsa_iodev_ucm_complete_init(node->iodev);
+ }
+
+error:
+ ucm_section_free_list(ucm_sections);
+ return rc;
}
/*
@@ -155,19 +417,14 @@ static void filter_mixer_names(snd_use_case_mgr_t *ucm,
struct cras_alsa_card *cras_alsa_card_create(
struct cras_alsa_card_info *info,
const char *device_config_dir,
- struct cras_device_blacklist *blacklist)
+ struct cras_device_blacklist *blacklist,
+ const char *ucm_suffix)
{
snd_ctl_t *handle = NULL;
- int rc, dev_idx;
+ int rc, n;
snd_ctl_card_info_t *card_info;
const char *card_name;
- snd_pcm_info_t *dev_info;
struct cras_alsa_card *alsa_card;
- const char *output_names_extra[] = {
- "IEC958",
- };
- size_t output_names_extra_size = ARRAY_SIZE(output_names_extra);
- char *extra_main_volume = NULL;
if (info->card_index >= MAX_ALSA_CARDS) {
syslog(LOG_ERR,
@@ -177,7 +434,6 @@ struct cras_alsa_card *cras_alsa_card_create(
}
snd_ctl_card_info_alloca(&card_info);
- snd_pcm_info_alloca(&dev_info);
alsa_card = calloc(1, sizeof(*alsa_card));
if (alsa_card == NULL)
@@ -214,67 +470,99 @@ struct cras_alsa_card *cras_alsa_card_create(
syslog(LOG_DEBUG, "No config file for %s", alsa_card->name);
/* Create a use case manager if a configuration is available. */
- alsa_card->ucm = ucm_create(card_name);
+ if (ucm_suffix) {
+ char *ucm_name;
+ if (asprintf(&ucm_name, "%s.%s", card_name, ucm_suffix) == -1) {
+ syslog(LOG_ERR, "Error creating ucm name");
+ goto error_bail;
+ }
+ alsa_card->ucm = ucm_create(ucm_name);
+ syslog(LOG_INFO, "Card %s (%s) has UCM: %s",
+ alsa_card->name, ucm_name,
+ alsa_card->ucm ? "yes" : "no");
+ free(ucm_name);
+ } else {
+ alsa_card->ucm = ucm_create(card_name);
+ syslog(LOG_INFO, "Card %s (%s) has UCM: %s",
+ alsa_card->name, card_name,
+ alsa_card->ucm ? "yes" : "no");
+ }
- /* Filter the extra output mixer names */
- if (alsa_card->ucm)
- filter_mixer_names(alsa_card->ucm, output_names_extra,
- output_names_extra_size);
- else
- output_names_extra_size = 0;
+ rc = snd_hctl_open(&alsa_card->hctl,
+ alsa_card->name,
+ SND_CTL_NONBLOCK);
+ if (rc < 0) {
+ syslog(LOG_DEBUG,
+ "failed to get hctl for %s", alsa_card->name);
+ alsa_card->hctl = NULL;
+ } else {
+ rc = snd_hctl_nonblock(alsa_card->hctl, 1);
+ if (rc < 0) {
+ syslog(LOG_ERR,
+ "failed to nonblock hctl for %s", alsa_card->name);
+ goto error_bail;
+ }
+
+ rc = snd_hctl_load(alsa_card->hctl);
+ if (rc < 0) {
+ syslog(LOG_ERR,
+ "failed to load hctl for %s", alsa_card->name);
+ goto error_bail;
+ }
+ }
- /* Check if an extra main volume has been specified. */
- if (alsa_card->ucm)
- extra_main_volume = ucm_get_flag(alsa_card->ucm,
- "ExtraMainVolume");
/* Create one mixer per card. */
- alsa_card->mixer = cras_alsa_mixer_create(alsa_card->name,
- alsa_card->config,
- output_names_extra,
- output_names_extra_size,
- extra_main_volume);
- free(extra_main_volume);
+ alsa_card->mixer = cras_alsa_mixer_create(alsa_card->name);
+
if (alsa_card->mixer == NULL) {
syslog(LOG_ERR, "Fail opening mixer for %s.", alsa_card->name);
goto error_bail;
}
- dev_idx = -1;
- while (1) {
- rc = snd_ctl_pcm_next_device(handle, &dev_idx);
- if (rc < 0) {
- cras_alsa_card_destroy(alsa_card);
- snd_ctl_close(handle);
- return NULL;
- }
- if (dev_idx < 0)
- break;
-
- snd_pcm_info_set_device(dev_info, dev_idx);
- snd_pcm_info_set_subdevice(dev_info, 0);
+ if (alsa_card->ucm && ucm_has_fully_specified_ucm_flag(alsa_card->ucm))
+ rc = add_controls_and_iodevs_with_ucm(
+ info, alsa_card, card_name, handle);
+ else
+ rc = add_controls_and_iodevs_by_matching(
+ info, blacklist, alsa_card, card_name, handle);
+ if (rc)
+ goto error_bail;
- /* Check for playback devices. */
- snd_pcm_info_set_stream(dev_info, SND_PCM_STREAM_PLAYBACK);
- if (snd_ctl_pcm_info(handle, dev_info) == 0 &&
- !should_ignore_dev(info, blacklist, dev_idx))
- create_iodev_for_device(alsa_card,
- info,
- card_name,
- snd_pcm_info_get_name(dev_info),
- snd_pcm_info_get_id(dev_info),
- dev_idx,
- CRAS_STREAM_OUTPUT);
+ n = alsa_card->hctl ?
+ snd_hctl_poll_descriptors_count(alsa_card->hctl) : 0;
+ if (n != 0 && card_has_hctl_jack(alsa_card)) {
+ struct hctl_poll_fd *registered_fd;
+ struct pollfd *pollfds;
+ int i;
+
+ pollfds = malloc(n * sizeof(*pollfds));
+ if (pollfds == NULL) {
+ rc = -ENOMEM;
+ goto error_bail;
+ }
- /* Check for capture devices. */
- snd_pcm_info_set_stream(dev_info, SND_PCM_STREAM_CAPTURE);
- if (snd_ctl_pcm_info(handle, dev_info) == 0)
- create_iodev_for_device(alsa_card,
- info,
- card_name,
- snd_pcm_info_get_name(dev_info),
- snd_pcm_info_get_id(dev_info),
- dev_idx,
- CRAS_STREAM_INPUT);
+ n = snd_hctl_poll_descriptors(alsa_card->hctl, pollfds, n);
+ for (i = 0; i < n; i++) {
+ registered_fd = calloc(1, sizeof(*registered_fd));
+ if (registered_fd == NULL) {
+ free(pollfds);
+ rc = -ENOMEM;
+ goto error_bail;
+ }
+ registered_fd->fd = pollfds[i].fd;
+ DL_APPEND(alsa_card->hctl_poll_fds, registered_fd);
+ rc = cras_system_add_select_fd(
+ registered_fd->fd,
+ alsa_control_event_pending,
+ alsa_card);
+ if (rc < 0) {
+ DL_DELETE(alsa_card->hctl_poll_fds,
+ registered_fd);
+ free(pollfds);
+ goto error_bail;
+ }
+ }
+ free(pollfds);
}
snd_ctl_close(handle);
@@ -283,19 +571,14 @@ struct cras_alsa_card *cras_alsa_card_create(
error_bail:
if (handle != NULL)
snd_ctl_close(handle);
- if (alsa_card->ucm)
- ucm_destroy(alsa_card->ucm);
- if (alsa_card->mixer)
- cras_alsa_mixer_destroy(alsa_card->mixer);
- if (alsa_card->config)
- cras_card_config_destroy(alsa_card->config);
- free(alsa_card);
+ cras_alsa_card_destroy(alsa_card);
return NULL;
}
void cras_alsa_card_destroy(struct cras_alsa_card *alsa_card)
{
struct iodev_list_node *curr;
+ struct hctl_poll_fd *poll_fd;
if (alsa_card == NULL)
return;
@@ -305,9 +588,17 @@ void cras_alsa_card_destroy(struct cras_alsa_card *alsa_card)
DL_DELETE(alsa_card->iodevs, curr);
free(curr);
}
+ DL_FOREACH(alsa_card->hctl_poll_fds, poll_fd) {
+ cras_system_rm_select_fd(poll_fd->fd);
+ DL_DELETE(alsa_card->hctl_poll_fds, poll_fd);
+ free(poll_fd);
+ }
+ if (alsa_card->hctl)
+ snd_hctl_close(alsa_card->hctl);
if (alsa_card->ucm)
ucm_destroy(alsa_card->ucm);
- cras_alsa_mixer_destroy(alsa_card->mixer);
+ if (alsa_card->mixer)
+ cras_alsa_mixer_destroy(alsa_card->mixer);
if (alsa_card->config)
cras_card_config_destroy(alsa_card->config);
free(alsa_card);
diff --git a/cras/src/server/cras_alsa_card.h b/cras/src/server/cras_alsa_card.h
index 7d0daf7b..705b8e78 100644
--- a/cras/src/server/cras_alsa_card.h
+++ b/cras/src/server/cras_alsa_card.h
@@ -25,6 +25,7 @@ struct cras_device_blacklist;
* device_config_dir - The directory of device configs which contains the
* volume curves.
* blacklist - List of devices that should be ignored.
+ * ucm_suffix - The ucm config name is formed as <card-name>.<suffix>
* Returns:
* A pointer to the newly created cras_alsa_card which must later be freed
* by calling cras_alsa_card_destroy or NULL on error.
@@ -32,7 +33,8 @@ struct cras_device_blacklist;
struct cras_alsa_card *cras_alsa_card_create(
struct cras_alsa_card_info *info,
const char *device_config_dir,
- struct cras_device_blacklist *blacklist);
+ struct cras_device_blacklist *blacklist,
+ const char *ucm_suffix);
/* Destroys a cras_alsa_card that was returned from cras_alsa_card_create.
* Args:
diff --git a/cras/src/server/cras_alsa_helpers.c b/cras/src/server/cras_alsa_helpers.c
index b3ec733f..a8719c2c 100644
--- a/cras/src/server/cras_alsa_helpers.c
+++ b/cras/src/server/cras_alsa_helpers.c
@@ -22,6 +22,9 @@
/* Assert the channel is defined in CRAS_CHANNELS. */
#define ALSA_CH_VALID(ch) ((ch >= SND_CHMAP_FL) && (ch <= SND_CHMAP_FRC))
+/* Time difference between two consecutive underrun logs. */
+#define UNDERRUN_LOG_TIME_SECS 30
+
/* Chances to give mmap_begin to work. */
static const size_t MAX_MMAP_BEGIN_ATTEMPTS = 3;
/* Time to sleep between resume attempts. */
@@ -82,6 +85,8 @@ static snd_pcm_chmap_query_t *cras_chmap_caps_match(
idx = fmt->channel_layout[ch];
if (idx == -1)
continue;
+ if ((unsigned)idx >= (*chmap)->map.channels)
+ continue;
if ((*chmap)->map.pos[idx] != CH_TO_ALSA(ch)) {
matches = 0;
break;
@@ -255,6 +260,47 @@ int cras_alsa_pcm_drain(snd_pcm_t *handle)
return snd_pcm_drain(handle);
}
+int cras_alsa_resume_appl_ptr(snd_pcm_t *handle, snd_pcm_uframes_t ahead)
+{
+ int rc;
+ snd_pcm_uframes_t period_frames, buffer_frames;
+ snd_pcm_sframes_t to_move, avail_frames;
+ rc = snd_pcm_avail(handle);
+ if (rc == -EPIPE || rc == -ESTRPIPE) {
+ cras_alsa_attempt_resume(handle);
+ avail_frames = 0;
+ } else if (rc < 0) {
+ syslog(LOG_ERR, "Fail to get avail frames: %s",
+ snd_strerror(rc));
+ return rc;
+ } else {
+ avail_frames = rc;
+ }
+
+ rc = snd_pcm_get_params(handle, &buffer_frames, &period_frames);
+ if (rc < 0) {
+ syslog(LOG_ERR, "Fail to get buffer size: %s",
+ snd_strerror(rc));
+ return rc;
+ }
+
+ to_move = avail_frames - buffer_frames + ahead;
+ if (to_move > 0) {
+ rc = snd_pcm_forward(handle, to_move);
+ } else if (to_move < 0) {
+ rc = snd_pcm_rewind(handle, -to_move);
+ } else {
+ return 0;
+ }
+
+ if (rc < 0) {
+ syslog(LOG_ERR, "Fail to resume appl_ptr: %s",
+ snd_strerror(rc));
+ return rc;
+ }
+ return 0;
+}
+
int cras_alsa_set_channel_map(snd_pcm_t *handle,
struct cras_audio_format *fmt)
{
@@ -417,7 +463,8 @@ int cras_alsa_fill_properties(const char *dev, snd_pcm_stream_t stream,
}
int cras_alsa_set_hwparams(snd_pcm_t *handle, struct cras_audio_format *format,
- snd_pcm_uframes_t *buffer_frames)
+ snd_pcm_uframes_t *buffer_frames, int period_wakeup,
+ unsigned int dma_period_time)
{
unsigned int rate, ret_rate;
int err;
@@ -444,13 +491,32 @@ int cras_alsa_set_hwparams(snd_pcm_t *handle, struct cras_audio_format *format,
syslog(LOG_ERR, "Setting interleaved %s\n", snd_strerror(err));
return err;
}
- /* Try to disable ALSA wakeups, we'll keep a timer. */
- if (snd_pcm_hw_params_can_disable_period_wakeup(hwparams)) {
+ /* If period_wakeup flag is not set, try to disable ALSA wakeups,
+ * we'll keep a timer. */
+ if (!period_wakeup &&
+ snd_pcm_hw_params_can_disable_period_wakeup(hwparams)) {
err = snd_pcm_hw_params_set_period_wakeup(handle, hwparams, 0);
if (err < 0)
syslog(LOG_WARNING, "disabling wakeups %s\n",
snd_strerror(err));
}
+ /* Setup the period time so that the hardware pulls the right amount
+ * of data at the right time. */
+ if (dma_period_time) {
+ int dir = 0;
+ unsigned int original = dma_period_time;
+
+ err = snd_pcm_hw_params_set_period_time_near(
+ handle, hwparams, &dma_period_time, &dir);
+ if (err < 0) {
+ syslog(LOG_ERR, "could not set period time: %s",
+ snd_strerror(err));
+ return err;
+ } else if (original != dma_period_time) {
+ syslog(LOG_DEBUG, "period time set to: %u",
+ dma_period_time);
+ }
+ }
/* Set the sample format. */
err = snd_pcm_hw_params_set_format(handle, hwparams,
format->format);
@@ -505,11 +571,10 @@ int cras_alsa_set_hwparams(snd_pcm_t *handle, struct cras_audio_format *format,
ret_rate, format->num_channels, format->format);
return err;
}
-
return 0;
}
-int cras_alsa_set_swparams(snd_pcm_t *handle)
+int cras_alsa_set_swparams(snd_pcm_t *handle, int *enable_htimestamp)
{
int err;
snd_pcm_sw_params_t *swparams;
@@ -546,7 +611,53 @@ int cras_alsa_set_swparams(snd_pcm_t *handle)
return err;
}
+ if (*enable_htimestamp) {
+ /* Use MONOTONIC_RAW time-stamps. */
+ err = snd_pcm_sw_params_set_tstamp_type(
+ handle, swparams,
+ SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW);
+ if (err < 0) {
+ syslog(LOG_ERR, "set_tstamp_type: %s\n",
+ snd_strerror(err));
+ return err;
+ }
+ err = snd_pcm_sw_params_set_tstamp_mode(
+ handle, swparams, SND_PCM_TSTAMP_ENABLE);
+ if (err < 0) {
+ syslog(LOG_ERR, "set_tstamp_mode: %s\n",
+ snd_strerror(err));
+ return err;
+ }
+ }
+
+ /* This hack is required because ALSA-LIB does not provide any way to
+ * detect whether MONOTONIC_RAW timestamps are supported by the kernel.
+ * In ALSA-LIB, the code checks the hardware protocol version. */
err = snd_pcm_sw_params(handle, swparams);
+ if (err == -EINVAL && *enable_htimestamp) {
+ *enable_htimestamp = 0;
+ syslog(LOG_WARNING,
+ "MONOTONIC_RAW timestamps are not supported.");
+
+ err = snd_pcm_sw_params_set_tstamp_type(
+ handle, swparams,
+ SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY);
+ if (err < 0) {
+ syslog(LOG_ERR, "set_tstamp_type: %s\n",
+ snd_strerror(err));
+ return err;
+ }
+ err = snd_pcm_sw_params_set_tstamp_mode(
+ handle, swparams, SND_PCM_TSTAMP_NONE);
+ if (err < 0) {
+ syslog(LOG_ERR, "set_tstamp_mode: %s\n",
+ snd_strerror(err));
+ return err;
+ }
+
+ err = snd_pcm_sw_params(handle, swparams);
+ }
+
if (err < 0) {
syslog(LOG_ERR, "sw_params: %s\n", snd_strerror(err));
return err;
@@ -555,22 +666,63 @@ int cras_alsa_set_swparams(snd_pcm_t *handle)
}
int cras_alsa_get_avail_frames(snd_pcm_t *handle, snd_pcm_uframes_t buf_size,
- snd_pcm_uframes_t *used)
+ snd_pcm_uframes_t severe_underrun_frames,
+ const char *dev_name,
+ snd_pcm_uframes_t *avail,
+ struct timespec *tstamp,
+ unsigned int *underruns)
{
snd_pcm_sframes_t frames;
int rc = 0;
+ static struct timespec tstamp_last_underrun_log =
+ {.tv_sec = 0, .tv_nsec = 0};
+ /* Use snd_pcm_avail still to ensure that the hardware pointer is
+ * up to date. Otherwise, we could use the deprecated snd_pcm_hwsync().
+ * IMO this is a deficiency in the ALSA API.
+ */
frames = snd_pcm_avail(handle);
- if (frames == -EPIPE || frames == -ESTRPIPE) {
- cras_alsa_attempt_resume(handle);
- frames = 0;
- } else if (frames < 0) {
- syslog(LOG_INFO, "pcm_avail error %s\n", snd_strerror(frames));
+ if (frames >= 0)
+ rc = snd_pcm_htimestamp(handle, avail, tstamp);
+ else
rc = frames;
- frames = 0;
- } else if (frames > (snd_pcm_sframes_t)buf_size)
- frames = buf_size;
- *used = frames;
+ if (rc == -EPIPE || rc == -ESTRPIPE) {
+ cras_alsa_attempt_resume(handle);
+ rc = 0;
+ goto error;
+ } else if (rc < 0) {
+ syslog(LOG_ERR, "pcm_avail error %s, %s\n",
+ dev_name, snd_strerror(rc));
+ goto error;
+ } else if (frames >= (snd_pcm_sframes_t)buf_size) {
+ struct timespec tstamp_now;
+ *underruns = *underruns + 1;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &tstamp_now);
+ /* Limit the log rate. */
+ if ((tstamp_now.tv_sec - tstamp_last_underrun_log.tv_sec) >
+ UNDERRUN_LOG_TIME_SECS) {
+ syslog(LOG_ERR,
+ "pcm_avail returned frames larger than buf_size: "
+ "%s: %ld > %lu for %u times\n",
+ dev_name, frames, buf_size, *underruns);
+ tstamp_last_underrun_log.tv_sec = tstamp_now.tv_sec;
+ tstamp_last_underrun_log.tv_nsec = tstamp_now.tv_nsec;
+ }
+ if ((frames - (snd_pcm_sframes_t)buf_size) >
+ (snd_pcm_sframes_t)severe_underrun_frames) {
+ rc = -EPIPE;
+ goto error;
+ } else {
+ frames = buf_size;
+ }
+ }
+ *avail = frames;
+ return 0;
+
+error:
+ *avail = 0;
+ tstamp->tv_sec = 0;
+ tstamp->tv_nsec = 0;
return rc;
}
@@ -607,6 +759,24 @@ int cras_alsa_attempt_resume(snd_pcm_t *handle)
return rc;
}
+int cras_alsa_mmap_get_whole_buffer(snd_pcm_t *handle, uint8_t **dst,
+ unsigned int *underruns)
+{
+ snd_pcm_uframes_t offset;
+ /* The purpose of calling cras_alsa_mmap_begin is to get the base
+ * address of the buffer. The requested and retrieved frames are not
+ * meaningful here.
+ * However, we need to set a non-zero requested frames to get a
+ * non-zero retrieved frames. This is to avoid the error checking in
+ * snd_pcm_mmap_begin, where it judges retrieved frames being 0 as a
+ * failure.
+ */
+ snd_pcm_uframes_t frames = 1;
+
+ return cras_alsa_mmap_begin(
+ handle, 0, dst, &offset, &frames, underruns);
+}
+
int cras_alsa_mmap_begin(snd_pcm_t *handle, unsigned int format_bytes,
uint8_t **dst, snd_pcm_uframes_t *offset,
snd_pcm_uframes_t *frames, unsigned int *underruns)
diff --git a/cras/src/server/cras_alsa_helpers.h b/cras/src/server/cras_alsa_helpers.h
index 6513a3e7..33c33908 100644
--- a/cras/src/server/cras_alsa_helpers.h
+++ b/cras/src/server/cras_alsa_helpers.h
@@ -73,6 +73,36 @@ int cras_alsa_pcm_start(snd_pcm_t *handle);
*/
int cras_alsa_pcm_drain(snd_pcm_t *handle);
+/* Forward/rewind appl_ptr so it becomes ahead of hw_ptr by fuzz samples.
+ * After moving appl_ptr, device can play the new samples as quick as possible.
+ * avail = buffer_frames - appl_ptr + hw_ptr
+ * => hw_ptr - appl_ptr = avail - buffer_frames.
+ * The difference between hw_ptr and app_ptr can be inferred from snd_pcm_avail.
+ * So the amount of frames to forward appl_ptr is
+ * avail - buffer_frames + fuzz.
+ * When hw_ptr is wrapped around boundary, this value may be negative. Use
+ * snd_pcm_rewind to move appl_ptr backward.
+ *
+ * Case 1: avail - buffer_frames + fuzz > 0
+ *
+ * -------|----------|-----------------------------------
+ * app_ptr hw_ptr
+ * |------------->| forward target
+ *
+ * Case 2: avail - buffer_frames + fuzz < 0
+ *
+ * -------|----------|-----------------------------------
+ * hw_ptr app_ptr
+ * |<------| rewind target
+ *
+ * Args:
+ * handle - Filled with a pointer to the opened pcm.
+ * ahead - Number of frames appl_ptr should be ahead of hw_ptr.
+ * Returns:
+ * 0 on success. A negative error code on failure.
+ */
+int cras_alsa_resume_appl_ptr(snd_pcm_t *handle, snd_pcm_uframes_t ahead);
+
/* Probes properties of the alsa device.
* Args:
* dev - Path to the alsa device to test.
@@ -95,30 +125,55 @@ int cras_alsa_fill_properties(const char *dev, snd_pcm_stream_t stream,
* handle - The open PCM to configure.
* format - The audio format desired for playback/capture.
* buffer_frames - Number of frames in the ALSA buffer.
+ * period_wakeup - Flag to determine if period_wakeup is required
+ * 0 - disable, 1 - enable
+ * dma_period_time - If non-zero, set the dma period time to this value
+ * (in microseconds).
* Returns:
* 0 on success, negative error on failure.
*/
int cras_alsa_set_hwparams(snd_pcm_t *handle, struct cras_audio_format *format,
- snd_pcm_uframes_t *buffer_frames);
+ snd_pcm_uframes_t *buffer_frames, int period_wakeup,
+ unsigned int dma_period_time);
/* Sets up the swparams to alsa.
* Args:
* handle - The open PCM to configure.
+ * enable_htimestamp - If non-zero, enable and configure hardware timestamps,
+ * updated to reflect whether MONOTONIC RAW htimestamps
+ * are supported by the kernel implementation.
* Returns:
* 0 on success, negative error on failure.
*/
-int cras_alsa_set_swparams(snd_pcm_t *handle);
+int cras_alsa_set_swparams(snd_pcm_t *handle, int *enable_htimestamp);
/* Get the number of used frames in the alsa buffer.
+ *
+ * When underrun is not severe, this function masks the underrun situation
+ * and set avail as 0. When underrun is severe, returns -EPIPE so caller
+ * can handle it.
* Args:
- * handle - The open PCM to configure.
- * buf_size - Number of frames in the ALSA buffer.
- * used - Filled with the number of used frames.
+ * handle[in] - The open PCM to configure.
+ * buf_size[in] - Number of frames in the ALSA buffer.
+ * severe_underrun_frames[in] - Number of frames as the threshold for severe
+ * underrun.
+ * dev_name[in] - Device name for logging.
+ * avail[out] - Filled with the number of frames available in the buffer.
+ * tstamp[out] - Filled with the hardware timestamp for the available frames.
+ * This value is {0, 0} when the device hasn't actually started
+ * reading or writing frames.
+ * underruns[in,out] - Pointer to the underrun counter updated if there was
+ * an underrun.
* Returns:
- * 0 on success, negative error on failure.
+ * 0 on success, negative error on failure. -EPIPE if severe underrun
+ * happens.
*/
int cras_alsa_get_avail_frames(snd_pcm_t *handle, snd_pcm_uframes_t buf_size,
- snd_pcm_uframes_t *used);
+ snd_pcm_uframes_t severe_underrun_frames,
+ const char *dev_name,
+ snd_pcm_uframes_t *avail,
+ struct timespec *tstamp,
+ unsigned int *underruns);
/* Get the current alsa delay, make sure it's no bigger than the buffer size.
* Args:
@@ -131,6 +186,18 @@ int cras_alsa_get_avail_frames(snd_pcm_t *handle, snd_pcm_uframes_t buf_size,
int cras_alsa_get_delay_frames(snd_pcm_t *handle, snd_pcm_uframes_t buf_size,
snd_pcm_sframes_t *delay);
+/* Wrapper for snd_pcm_mmap_begin where only buffer is concerned.
+ * Offset and frames from cras_alsa_mmap_begin are neglected.
+ * Args:
+ * handle - The open PCM to configure.
+ * dst - Pointer set to the area for reading/writing the audio.
+ * underruns - counter to increment if an under-run occurs.
+ * Returns:
+ * zero on success, negative error code for fatal errors.
+ */
+int cras_alsa_mmap_get_whole_buffer(snd_pcm_t *handle, uint8_t **dst,
+ unsigned int *underrun);
+
/* Wrapper for snd_pcm_mmap_begin
* Args:
* handle - The open PCM to configure.
diff --git a/cras/src/server/cras_alsa_io.c b/cras/src/server/cras_alsa_io.c
index ff4b3238..80a8c3b3 100644
--- a/cras/src/server/cras_alsa_io.c
+++ b/cras/src/server/cras_alsa_io.c
@@ -4,7 +4,6 @@
*/
#include <alsa/asoundlib.h>
-#include <alsa/use-case.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
@@ -23,10 +22,11 @@
#include "cras_alsa_ucm.h"
#include "cras_audio_area.h"
#include "cras_config.h"
-#include "cras_dbus_util.h"
+#include "cras_utf8.h"
#include "cras_iodev.h"
#include "cras_iodev_list.h"
#include "cras_messages.h"
+#include "cras_ramp.h"
#include "cras_rclient.h"
#include "cras_shm.h"
#include "cras_system_state.h"
@@ -38,7 +38,7 @@
#include "utlist.h"
#define MAX_ALSA_DEV_NAME_LENGTH 9 /* Alsa names "hw:XX,YY" + 1 for null. */
-#define AOKR_DEV "Wake on Voice"
+#define HOTWORD_DEV "Wake on Voice"
#define DEFAULT "(default)"
#define HDMI "HDMI"
#define INTERNAL_MICROPHONE "Internal Mic"
@@ -46,24 +46,34 @@
#define KEYBOARD_MIC "Keyboard Mic"
#define USB "USB"
-/* For USB, pad the output buffer. This avoids a situation where there isn't a
+/*
+ * For USB, pad the output buffer. This avoids a situation where there isn't a
* complete URB's worth of audio ready to be transmitted when it is requested.
* The URB interval does track directly to the audio clock, making it hard to
- * predict the exact interval. */
+ * predict the exact interval.
+ */
#define USB_EXTRA_BUFFER_FRAMES 768
+/*
+ * When snd_pcm_avail returns a value that is greater than buffer size,
+ * we know there is an underrun. If the number of underrun samples
+ * (avail - buffer_size) is greater than SEVERE_UNDERRUN_MS * rate,
+ * it is a severe underrun. Main thread should disable and then enable
+ * device to recover it from underrun.
+ */
+#define SEVERE_UNDERRUN_MS 5000
-/* This extends cras_ionode to include alsa-specific information.
+/*
+ * This extends cras_ionode to include alsa-specific information.
* Members:
* mixer_output - From cras_alsa_mixer.
- * jack_curve - In absense of a mixer output, holds a volume curve to use
- * when this jack is plugged.
- * jack - The jack associated with the jack_curve (if it exists).
+ * volume_curve - Volume curve for this node.
+ * jack - The jack associated with the node.
*/
struct alsa_output_node {
struct cras_ionode base;
struct mixer_control *mixer_output;
- struct cras_volume_curve *jack_curve;
+ struct cras_volume_curve *volume_curve;
const struct cras_alsa_jack *jack;
};
@@ -71,52 +81,175 @@ struct alsa_input_node {
struct cras_ionode base;
struct mixer_control* mixer_input;
const struct cras_alsa_jack *jack;
+ int8_t *channel_layout;
};
-/* Child of cras_iodev, alsa_io handles ALSA interaction for sound devices.
+/*
+ * Child of cras_iodev, alsa_io handles ALSA interaction for sound devices.
* base - The cras_iodev structure "base class".
* dev - String that names this device (e.g. "hw:0,0").
+ * dev_name - value from snd_pcm_info_get_name
+ * dev_id - value from snd_pcm_info_get_id
* device_index - ALSA index of device, Y in "hw:X:Y".
* next_ionode_index - The index we will give to the next ionode. Each ionode
* have a unique index within the iodev.
* card_type - the type of the card this iodev belongs.
* 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.
+ * enable_htimestamp - True when the device's htimestamp is used.
* handle - Handle to the opened ALSA device.
* num_underruns - Number of times we have run out of data (playback only).
+ * num_severe_underruns - Number of times we have run out of data badly.
+ Unlike num_underruns which records for the duration
+ where device is opened, num_severe_underruns records
+ since device is created. When severe underrun occurs
+ a possible action is to close/open device.
* alsa_stream - Playback or capture type.
* mixer - Alsa mixer used to control volume and mute of the device.
+ * config - Card config for this alsa device.
* jack_list - List of alsa jack controls for this device.
- * ucm - ALSA use case manager, if configuration is found.
+ * ucm - CRAS use case manager, if configuration is found.
* mmap_offset - offset returned from mmap_begin.
* dsp_name_default - the default dsp name for the device. It can be overridden
* by the jack specific dsp name.
* poll_fd - Descriptor used to block until data is ready.
+ * dma_period_set_microsecs - If non-zero, the value to apply to the dma_period.
+ * is_free_running - true if device is playing zeros in the buffer without
+ * user filling meaningful data. The device buffer is filled
+ * with zeros. In this state, appl_ptr remains the same
+ * while hw_ptr keeps running ahead.
+ * filled_zeros_for_draining - The number of zeros filled for draining.
+ * severe_underrun_frames - The threshold for severe underrun.
+ * default_volume_curve - Default volume curve that converts from an index
+ * to dBFS.
*/
struct alsa_io {
struct cras_iodev base;
char *dev;
+ char *dev_name;
+ char *dev_id;
uint32_t device_index;
uint32_t next_ionode_index;
enum CRAS_ALSA_CARD_TYPE card_type;
int is_first;
+ int fully_specified;
+ int enable_htimestamp;
snd_pcm_t *handle;
unsigned int num_underruns;
+ unsigned int num_severe_underruns;
snd_pcm_stream_t alsa_stream;
struct cras_alsa_mixer *mixer;
+ const struct cras_card_config *config;
struct cras_alsa_jack_list *jack_list;
- snd_use_case_mgr_t *ucm;
+ struct cras_use_case_mgr *ucm;
snd_pcm_uframes_t mmap_offset;
const char *dsp_name_default;
int poll_fd;
+ unsigned int dma_period_set_microsecs;
+ int is_free_running;
+ unsigned int filled_zeros_for_draining;
+ snd_pcm_uframes_t severe_underrun_frames;
+ struct cras_volume_curve *default_volume_curve;
};
static void init_device_settings(struct alsa_io *aio);
+static int alsa_iodev_set_active_node(struct cras_iodev *iodev,
+ struct cras_ionode *ionode,
+ unsigned dev_enabled);
+
+/*
+ * Defines the default values of nodes.
+ */
+static const struct {
+ const char *name;
+ enum CRAS_NODE_TYPE type;
+ enum CRAS_NODE_POSITION position;
+} node_defaults[] = {
+ {
+ .name = DEFAULT,
+ .type = CRAS_NODE_TYPE_UNKNOWN,
+ .position = NODE_POSITION_INTERNAL,
+ },
+ {
+ .name = INTERNAL_SPEAKER,
+ .type = CRAS_NODE_TYPE_INTERNAL_SPEAKER,
+ .position = NODE_POSITION_INTERNAL,
+ },
+ {
+ .name = INTERNAL_MICROPHONE,
+ .type = CRAS_NODE_TYPE_MIC,
+ .position = NODE_POSITION_INTERNAL,
+ },
+ {
+ .name = KEYBOARD_MIC,
+ .type = CRAS_NODE_TYPE_MIC,
+ .position = NODE_POSITION_KEYBOARD,
+ },
+ {
+ .name = HDMI,
+ .type = CRAS_NODE_TYPE_HDMI,
+ .position = NODE_POSITION_EXTERNAL,
+ },
+ {
+ .name = "IEC958",
+ .type = CRAS_NODE_TYPE_HDMI,
+ .position = NODE_POSITION_EXTERNAL,
+ },
+ {
+ .name = "Headphone",
+ .type = CRAS_NODE_TYPE_HEADPHONE,
+ .position = NODE_POSITION_EXTERNAL,
+ },
+ {
+ .name = "Front Headphone",
+ .type = CRAS_NODE_TYPE_HEADPHONE,
+ .position = NODE_POSITION_EXTERNAL,
+ },
+ {
+ .name = "Front Mic",
+ .type = CRAS_NODE_TYPE_MIC,
+ .position = NODE_POSITION_FRONT,
+ },
+ {
+ .name = "Rear Mic",
+ .type = CRAS_NODE_TYPE_MIC,
+ .position = NODE_POSITION_REAR,
+ },
+ {
+ .name = "Mic",
+ .type = CRAS_NODE_TYPE_MIC,
+ .position = NODE_POSITION_EXTERNAL,
+ },
+ {
+ .name = HOTWORD_DEV,
+ .type = CRAS_NODE_TYPE_HOTWORD,
+ .position = NODE_POSITION_INTERNAL,
+ },
+ {
+ .name = "Haptic",
+ .type = CRAS_NODE_TYPE_HAPTIC,
+ .position = NODE_POSITION_INTERNAL,
+ },
+ {
+ .name = "Rumbler",
+ .type = CRAS_NODE_TYPE_HAPTIC,
+ .position = NODE_POSITION_INTERNAL,
+ },
+ {
+ .name = "Line Out",
+ .type = CRAS_NODE_TYPE_LINEOUT,
+ .position = NODE_POSITION_EXTERNAL,
+ },
+};
+
/*
* iodev callbacks.
*/
-static int frames_queued(const struct cras_iodev *iodev)
+static int frames_queued(const struct cras_iodev *iodev,
+ struct timespec *tstamp)
{
struct alsa_io *aio = (struct alsa_io *)iodev;
int rc;
@@ -124,10 +257,17 @@ static int frames_queued(const struct cras_iodev *iodev)
rc = cras_alsa_get_avail_frames(aio->handle,
aio->base.buffer_size,
- &frames);
- if (rc < 0)
+ aio->severe_underrun_frames,
+ iodev->info.name,
+ &frames, tstamp,
+ &aio->num_underruns);
+ if (rc < 0) {
+ if (rc == -EPIPE)
+ aio->num_severe_underruns++;
return rc;
-
+ }
+ if (!aio->enable_htimestamp)
+ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
if (iodev->direction == CRAS_STREAM_INPUT)
return (int)frames;
@@ -154,18 +294,23 @@ static int close_dev(struct cras_iodev *iodev)
{
struct alsa_io *aio = (struct alsa_io *)iodev;
+ /* Removes audio thread callback from main thread. */
if (aio->poll_fd >= 0)
- audio_thread_rm_callback(aio->poll_fd);
+ audio_thread_rm_callback_sync(
+ cras_iodev_list_get_audio_thread(),
+ aio->poll_fd);
if (!aio->handle)
return 0;
cras_alsa_pcm_close(aio->handle);
aio->handle = NULL;
+ aio->is_free_running = 0;
+ aio->filled_zeros_for_draining = 0;
cras_iodev_free_format(&aio->base);
cras_iodev_free_audio_area(&aio->base);
return 0;
}
-static int dummy_aokr_cb(void *arg)
+static int dummy_hotword_cb(void *arg)
{
/* Only need this once. */
struct alsa_io *aio = (struct alsa_io *)arg;
@@ -178,6 +323,7 @@ static int open_dev(struct cras_iodev *iodev)
{
struct alsa_io *aio = (struct alsa_io *)iodev;
snd_pcm_t *handle;
+ int period_wakeup;
int rc;
/* This is called after the first stream added so configure for it.
@@ -186,6 +332,11 @@ static int open_dev(struct cras_iodev *iodev)
if (iodev->format == NULL)
return -EINVAL;
aio->num_underruns = 0;
+ aio->is_free_running = 0;
+ aio->filled_zeros_for_draining = 0;
+ aio->severe_underrun_frames =
+ SEVERE_UNDERRUN_MS * iodev->format->frame_rate / 1000;
+
cras_iodev_init_audio_area(iodev, iodev->format->num_channels);
syslog(LOG_DEBUG, "Configure alsa device %s rate %zuHz, %zu channels",
@@ -196,8 +347,12 @@ static int open_dev(struct cras_iodev *iodev)
if (rc < 0)
return rc;
+ /* If it's a wake on voice device, period_wakeups are required. */
+ period_wakeup = (iodev->active_node->type == CRAS_NODE_TYPE_HOTWORD);
+
rc = cras_alsa_set_hwparams(handle, iodev->format,
- &iodev->buffer_size);
+ &iodev->buffer_size, period_wakeup,
+ aio->dma_period_set_microsecs);
if (rc < 0) {
cras_alsa_pcm_close(handle);
return rc;
@@ -212,7 +367,7 @@ static int open_dev(struct cras_iodev *iodev)
}
/* Configure software params. */
- rc = cras_alsa_set_swparams(handle);
+ rc = cras_alsa_set_swparams(handle, &aio->enable_htimestamp);
if (rc < 0) {
cras_alsa_pcm_close(handle);
return rc;
@@ -223,7 +378,7 @@ static int open_dev(struct cras_iodev *iodev)
init_device_settings(aio);
aio->poll_fd = -1;
- if (iodev->active_node->type == CRAS_NODE_TYPE_AOKR) {
+ if (iodev->active_node->type == CRAS_NODE_TYPE_HOTWORD) {
struct pollfd *ufds;
int count, i;
@@ -255,7 +410,8 @@ static int open_dev(struct cras_iodev *iodev)
free(ufds);
if (aio->poll_fd >= 0)
- audio_thread_add_callback(aio->poll_fd, dummy_aokr_cb,
+ audio_thread_add_callback(aio->poll_fd,
+ dummy_hotword_cb,
aio);
}
@@ -266,38 +422,45 @@ static int open_dev(struct cras_iodev *iodev)
return 0;
}
-static int is_open(const struct cras_iodev *iodev)
+/*
+ * Check if ALSA device is opened by checking if handle is valid.
+ * Note that to fully open a cras_iodev, ALSA device is opened first, then there
+ * are some device init settings to be done in init_device_settings.
+ * Therefore, when setting volume/mute/gain in init_device_settings,
+ * cras_iodev is not in CRAS_IODEV_STATE_OPEN yet. We need to check if handle
+ * is valid when setting those properties, instead of checking
+ * cras_iodev_is_open.
+ */
+static int has_handle(const struct alsa_io *aio)
{
- struct alsa_io *aio = (struct alsa_io *)iodev;
-
return !!aio->handle;
}
-static int dev_running(const struct cras_iodev *iodev)
+static int start(const struct cras_iodev *iodev)
{
struct alsa_io *aio = (struct alsa_io *)iodev;
snd_pcm_t *handle = aio->handle;
int rc;
if (snd_pcm_state(handle) == SND_PCM_STATE_RUNNING)
- return 1;
+ return 0;
if (snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) {
rc = cras_alsa_attempt_resume(handle);
if (rc < 0) {
syslog(LOG_ERR, "Resume error: %s", snd_strerror(rc));
- return 0;
+ return rc;
}
cras_iodev_reset_rate_estimator(iodev);
} else {
rc = cras_alsa_pcm_start(handle);
if (rc < 0) {
syslog(LOG_ERR, "Start error: %s", snd_strerror(rc));
- return 0;
+ return rc;
}
}
- return 1;
+ return 0;
}
static int get_buffer(struct cras_iodev *iodev,
@@ -351,9 +514,10 @@ static int flush_buffer(struct cras_iodev *iodev)
return 0;
}
- /* Gets the first plugged node in list. This is used as the
- * default node to set as active.
- */
+/*
+ * Gets the first plugged node in list. This is used as the
+ * default node to set as active.
+ */
static struct cras_ionode *first_plugged_node(struct cras_iodev *iodev)
{
struct cras_ionode *n;
@@ -368,19 +532,21 @@ static struct cras_ionode *first_plugged_node(struct cras_iodev *iodev)
return iodev->nodes;
}
-static void update_active_node(struct cras_iodev *iodev, unsigned node_idx)
+static void update_active_node(struct cras_iodev *iodev, unsigned node_idx,
+ unsigned dev_enabled)
{
struct cras_ionode *n;
/* If a node exists for node_idx, set it as active. */
DL_FOREACH(iodev->nodes, n) {
if (n->idx == node_idx) {
- alsa_iodev_set_active_node(iodev, n);
+ alsa_iodev_set_active_node(iodev, n, dev_enabled);
return;
}
}
- alsa_iodev_set_active_node(iodev, first_plugged_node(iodev));
+ alsa_iodev_set_active_node(iodev, first_plugged_node(iodev),
+ dev_enabled);
}
static int update_channel_layout(struct cras_iodev *iodev)
@@ -390,6 +556,20 @@ static int update_channel_layout(struct cras_iodev *iodev)
snd_pcm_uframes_t buf_size = 0;
int err = 0;
+ /* If the capture channel map is specified in UCM, prefer it over
+ * what ALSA provides. */
+ if (aio->ucm && (iodev->direction == CRAS_STREAM_INPUT)) {
+ struct alsa_input_node *input =
+ (struct alsa_input_node *)iodev->active_node;
+
+ if (input->channel_layout) {
+ memcpy(iodev->format->channel_layout,
+ input->channel_layout,
+ CRAS_CH_MAX * sizeof(*input->channel_layout));
+ return 0;
+ }
+ }
+
err = cras_alsa_pcm_open(&handle, aio->dev, aio->alsa_stream);
if (err < 0) {
syslog(LOG_ERR, "snd_pcm_open_failed: %s", snd_strerror(err));
@@ -398,7 +578,8 @@ static int update_channel_layout(struct cras_iodev *iodev)
/* Sets frame rate and channel count to alsa device before
* we test channel mapping. */
- err = cras_alsa_set_hwparams(handle, iodev->format, &buf_size);
+ err = cras_alsa_set_hwparams(handle, iodev->format, &buf_size, 0,
+ aio->dma_period_set_microsecs);
if (err < 0) {
cras_alsa_pcm_close(handle);
return err;
@@ -410,6 +591,24 @@ static int update_channel_layout(struct cras_iodev *iodev)
return err;
}
+static int set_hotword_model(struct cras_iodev *iodev, const char *model_name)
+{
+ struct alsa_io *aio = (struct alsa_io *)iodev;
+ if (!aio->ucm)
+ return -EINVAL;
+
+ return ucm_set_hotword_model(aio->ucm, model_name);
+}
+
+static char *get_hotword_models(struct cras_iodev *iodev)
+{
+ struct alsa_io *aio = (struct alsa_io *)iodev;
+ if (!aio->ucm)
+ return NULL;
+
+ return ucm_get_hotword_models(aio->ucm);
+}
+
/*
* Alsa helper functions.
*/
@@ -424,38 +623,38 @@ static struct alsa_input_node *get_active_input(const struct alsa_io *aio)
return (struct alsa_input_node *)aio->base.active_node;
}
-/* Gets the curve for the active output. */
+/*
+ * Gets the curve for the active output node. If the node doesn't have volume
+ * curve specified, return the default volume curve of the parent iodev.
+ */
static const struct cras_volume_curve *get_curve_for_output_node(
const struct alsa_io *aio,
- const struct alsa_output_node *aout)
-{
- struct cras_volume_curve *curve = NULL;
- if (aout) {
- curve = cras_alsa_mixer_get_output_volume_curve(
- aout->mixer_output);
- if (curve)
- return curve;
- else if (aout->jack_curve)
- return aout->jack_curve;
- }
- return cras_alsa_mixer_default_volume_curve(aio->mixer);
+ const struct alsa_output_node *node)
+{
+ if (node && node->volume_curve)
+ return node->volume_curve;
+ return aio->default_volume_curve;
}
-/* Gets the curve for the active output. */
+/*
+ * Gets the curve for the active output.
+ */
static const struct cras_volume_curve *get_curve_for_active_output(
const struct alsa_io *aio)
{
- struct alsa_output_node *aout = get_active_output(aio);
- return get_curve_for_output_node(aio, aout);
+ struct alsa_output_node *node = get_active_output(aio);
+ return get_curve_for_output_node(aio, node);
}
-/* Informs the system of the volume limits for this device. */
+/*
+ * Informs the system of the volume limits for this device.
+ */
static void set_alsa_volume_limits(struct alsa_io *aio)
{
const struct cras_volume_curve *curve;
/* Only set the limits if the dev is active. */
- if (!is_open(&aio->base))
+ if (!has_handle(aio))
return;
curve = get_curve_for_active_output(aio);
@@ -464,12 +663,14 @@ static void set_alsa_volume_limits(struct alsa_io *aio)
curve->get_dBFS(curve, CRAS_MAX_SYSTEM_VOLUME));
}
-/* Sets the alsa mute state for this iodev. */
-static void set_alsa_mute(const struct alsa_io *aio, int muted)
+/*
+ * Sets the alsa mute control for this iodev.
+ */
+static void set_alsa_mute_control(const struct alsa_io *aio, int muted)
{
struct alsa_output_node *aout;
- if (!is_open(&aio->base))
+ if (!has_handle(aio))
return;
aout = get_active_output(aio);
@@ -479,16 +680,16 @@ static void set_alsa_mute(const struct alsa_io *aio, int muted)
aout ? aout->mixer_output : NULL);
}
-/* Sets the volume of the playback device to the specified level. Receives a
+/*
+ * Sets the volume of the playback device to the specified level. Receives a
* volume index from the system settings, ranging from 0 to 100, converts it to
- * dB using the volume curve, and sends the dB value to alsa. Handles mute and
- * unmute, including muting when volume is zero. */
+ * dB using the volume curve, and sends the dB value to alsa.
+ */
static void set_alsa_volume(struct cras_iodev *iodev)
{
const struct alsa_io *aio = (const struct alsa_io *)iodev;
const struct cras_volume_curve *curve;
size_t volume;
- int mute;
struct alsa_output_node *aout;
assert(aio);
@@ -496,11 +697,10 @@ static void set_alsa_volume(struct cras_iodev *iodev)
return;
/* Only set the volume if the dev is active. */
- if (!is_open(&aio->base))
+ if (!has_handle(aio))
return;
volume = cras_system_get_volume();
- mute = cras_system_get_mute();
curve = get_curve_for_active_output(aio);
if (curve == NULL)
return;
@@ -517,13 +717,20 @@ static void set_alsa_volume(struct cras_iodev *iodev)
aio->mixer,
curve->get_dBFS(curve, volume),
aout ? aout->mixer_output : NULL);
+}
+
+static void set_alsa_mute(struct cras_iodev *iodev)
+{
/* Mute for zero. */
- set_alsa_mute(aio, mute || (volume == 0));
+ const struct alsa_io *aio = (const struct alsa_io *)iodev;
+ set_alsa_mute_control(aio, cras_system_get_mute());
}
-/* Sets the capture gain to the current system input gain level, given in dBFS.
+/*
+ * Sets the capture gain to the current system input gain level, given in dBFS.
* Set mute based on the system mute state. This gain can be positive or
- * negative and might be adjusted often if and app is running an AGC. */
+ * negative and might be adjusted often if an app is running an AGC.
+ */
static void set_alsa_capture_gain(struct cras_iodev *iodev)
{
const struct alsa_io *aio = (const struct alsa_io *)iodev;
@@ -535,13 +742,17 @@ static void set_alsa_capture_gain(struct cras_iodev *iodev)
return;
/* Only set the volume if the dev is active. */
- if (!is_open(&aio->base))
+ if (!has_handle(aio))
return;
+ gain = cras_iodev_adjust_active_node_gain(
+ iodev, cras_system_get_capture_gain());
+
+ /* Set hardware gain to 0dB if software gain is needed. */
+ if (cras_iodev_software_volume_needed(iodev))
+ gain = 0;
- gain = cras_system_get_capture_gain();
ain = get_active_input(aio);
- if (ain)
- gain += ain->base.capture_gain;
+
cras_alsa_mixer_set_capture_dBFS(
aio->mixer,
gain,
@@ -551,7 +762,9 @@ static void set_alsa_capture_gain(struct cras_iodev *iodev)
ain ? ain->mixer_input : NULL);
}
-/* Swaps the left and right channels of the given node. */
+/*
+ * Swaps the left and right channels of the given node.
+ */
static int set_alsa_node_swapped(struct cras_iodev *iodev,
struct cras_ionode *node, int enable)
{
@@ -560,8 +773,10 @@ static int set_alsa_node_swapped(struct cras_iodev *iodev,
return ucm_enable_swap_mode(aio->ucm, node->name, enable);
}
-/* Initializes the device settings and registers for callbacks when system
- * settings have been changed.
+/*
+ * Initializes the device settings according to system volume, mute, gain
+ * settings.
+ * Updates system capture gain limits based on current active device/node.
*/
static void init_device_settings(struct alsa_io *aio)
{
@@ -570,16 +785,29 @@ static void init_device_settings(struct alsa_io *aio)
if (aio->base.direction == CRAS_STREAM_OUTPUT) {
set_alsa_volume_limits(aio);
set_alsa_volume(&aio->base);
+ set_alsa_mute(&aio->base);
} else {
struct mixer_control *mixer_input = NULL;
struct alsa_input_node *ain = get_active_input(aio);
+ long min_capture_gain, max_capture_gain;
+
if (ain)
mixer_input = ain->mixer_input;
- cras_system_set_capture_gain_limits(
- cras_alsa_mixer_get_minimum_capture_gain(aio->mixer,
- mixer_input),
- cras_alsa_mixer_get_maximum_capture_gain(aio->mixer,
- mixer_input));
+
+ if (cras_iodev_software_volume_needed(&aio->base)) {
+ min_capture_gain = DEFAULT_MIN_CAPTURE_GAIN;
+ max_capture_gain = cras_iodev_maximum_software_gain(
+ &aio->base);
+ } else {
+ min_capture_gain =
+ cras_alsa_mixer_get_minimum_capture_gain(
+ aio->mixer, mixer_input);
+ max_capture_gain =
+ cras_alsa_mixer_get_maximum_capture_gain(
+ aio->mixer, mixer_input);
+ }
+ cras_system_set_capture_gain_limits(min_capture_gain,
+ max_capture_gain);
set_alsa_capture_gain(&aio->base);
}
}
@@ -588,7 +816,8 @@ static void init_device_settings(struct alsa_io *aio)
* Functions run in the main server context.
*/
-/* Frees resources used by the alsa iodev.
+/*
+ * Frees resources used by the alsa iodev.
* Args:
* iodev - the iodev to free the resources from.
*/
@@ -604,7 +833,7 @@ static void free_alsa_iodev_resources(struct alsa_io *aio)
DL_FOREACH(aio->base.nodes, node) {
if (aio->base.direction == CRAS_STREAM_OUTPUT) {
aout = (struct alsa_output_node *)node;
- cras_volume_curve_destroy(aout->jack_curve);
+ cras_volume_curve_destroy(aout->volume_curve);
}
cras_iodev_rm_node(&aio->base, node);
free(node->softvol_scalers);
@@ -614,15 +843,23 @@ static void free_alsa_iodev_resources(struct alsa_io *aio)
free((void *)aio->dsp_name_default);
cras_iodev_free_resources(&aio->base);
free(aio->dev);
+ if (aio->dev_id)
+ free(aio->dev_id);
+ if (aio->dev_name)
+ free(aio->dev_name);
}
-/* Returns true if this is the first internal device */
+/*
+ * Returns true if this is the first internal device.
+ */
static int first_internal_device(struct alsa_io *aio)
{
return aio->is_first && aio->card_type == ALSA_CARD_TYPE_INTERNAL;
}
-/* Returns true if there is already a node created with the given name */
+/*
+ * Returns true if there is already a node created with the given name.
+ */
static int has_node(struct alsa_io *aio, const char *name)
{
struct cras_ionode *node;
@@ -634,7 +871,9 @@ static int has_node(struct alsa_io *aio, const char *name)
return 0;
}
-/* Returns true if string s ends with the given suffix */
+/*
+ * Returns true if string s ends with the given suffix.
+ */
int endswith(const char *s, const char *suffix)
{
size_t n = strlen(s);
@@ -642,7 +881,9 @@ int endswith(const char *s, const char *suffix)
return n >= m && !strcmp(s + (n - m), suffix);
}
-/* Drop the node name and replace it with node type. */
+/*
+ * Drop the node name and replace it with node type.
+ */
static void drop_node_name(struct cras_ionode *node)
{
if (node->type == CRAS_NODE_TYPE_USB)
@@ -657,28 +898,14 @@ static void drop_node_name(struct cras_ionode *node)
}
}
-/* Sets the initial plugged state and type of a node based on its
+/*
+ * Sets the initial plugged state and type of a node based on its
* name. Chrome will assign priority to nodes base on node type.
*/
static void set_node_initial_state(struct cras_ionode *node,
enum CRAS_ALSA_CARD_TYPE card_type)
{
- static const struct {
- const char *name;
- int initial_plugged;
- enum CRAS_NODE_TYPE type;
- } node_defaults[] = {
- { DEFAULT, 1, CRAS_NODE_TYPE_UNKNOWN},
- { INTERNAL_SPEAKER, 1, CRAS_NODE_TYPE_INTERNAL_SPEAKER },
- { INTERNAL_MICROPHONE, 1, CRAS_NODE_TYPE_INTERNAL_MIC },
- { KEYBOARD_MIC, 1, CRAS_NODE_TYPE_KEYBOARD_MIC },
- { HDMI, 0, CRAS_NODE_TYPE_HDMI },
- { "IEC958", 0, CRAS_NODE_TYPE_HDMI },
- { "Headphone", 0, CRAS_NODE_TYPE_HEADPHONE },
- { "Front Headphone", 0, CRAS_NODE_TYPE_HEADPHONE },
- { "Mic", 0, CRAS_NODE_TYPE_MIC },
- { AOKR_DEV, 1, CRAS_NODE_TYPE_AOKR },
- };
+
unsigned i;
node->volume = 100;
@@ -687,7 +914,9 @@ static void set_node_initial_state(struct cras_ionode *node,
for (i = 0; i < ARRAY_SIZE(node_defaults); i++)
if (!strncmp(node->name, node_defaults[i].name,
strlen(node_defaults[i].name))) {
- node->plugged = node_defaults[i].initial_plugged;
+ node->position = node_defaults[i].position;
+ node->plugged = (node->position
+ != NODE_POSITION_EXTERNAL);
node->type = node_defaults[i].type;
if (node->plugged)
gettimeofday(&node->plugged_time, NULL);
@@ -715,40 +944,15 @@ static void set_node_initial_state(struct cras_ionode *node,
/* Regardless of the node name of a USB headset (it can be "Speaker"),
* set it's type to usb.
*/
- if (card_type == ALSA_CARD_TYPE_USB)
+ if (card_type == ALSA_CARD_TYPE_USB) {
node->type = CRAS_NODE_TYPE_USB;
+ node->position = NODE_POSITION_EXTERNAL;
+ }
if (!is_utf8_string(node->name))
drop_node_name(node);
}
-static const char *get_output_node_name(struct alsa_io *aio,
- struct mixer_control *cras_output)
-{
- if (cras_output)
- return cras_alsa_mixer_get_control_name(cras_output);
-
- if (first_internal_device(aio) && !has_node(aio, INTERNAL_SPEAKER)) {
- if (strstr(aio->base.info.name, HDMI))
- return HDMI;
- return INTERNAL_SPEAKER;
- } else {
- return DEFAULT;
- }
-}
-
-static const char *get_input_node_name(struct alsa_io *aio,
- struct mixer_control *cras_input)
-{
- if (cras_input)
- return cras_alsa_mixer_get_control_name(cras_input);
-
- if (first_internal_device(aio) && !has_node(aio, INTERNAL_MICROPHONE))
- return INTERNAL_MICROPHONE;
- else
- return DEFAULT;
-}
-
static int get_ucm_flag_integer(struct alsa_io *aio,
const char *flag_name,
int *result)
@@ -815,8 +1019,11 @@ static void set_output_node_software_volume_needed(
return;
}
- /* Use software volume for HDMI output */
- if (output->base.type == CRAS_NODE_TYPE_HDMI)
+ /* Use software volume for HDMI output and nodes without volume mixer
+ * control. */
+ if ((output->base.type == CRAS_NODE_TYPE_HDMI) ||
+ (!cras_alsa_mixer_has_main_volume(mixer) &&
+ !cras_alsa_mixer_has_volume(output->mixer_output)))
output->base.software_volume_needed = 1;
/* Use software volume if the usb device's volume range is smaller
@@ -833,101 +1040,241 @@ static void set_output_node_software_volume_needed(
output->base.name);
}
-/* Callback for listing mixer outputs. The mixer will call this once for each
- * output associated with this device. Most commonly this is used to tell the
- * device it has Headphones and Speakers. */
-static void new_output(struct mixer_control *cras_output,
- void *callback_arg)
+static void set_input_node_software_volume_needed(
+ struct alsa_input_node *input, struct alsa_io *aio)
{
- struct alsa_io *aio;
- struct alsa_output_node *output;
- const char *name;
+ long max_software_gain;
+ int rc;
+
+ input->base.software_volume_needed = 0;
+ input->base.max_software_gain = 0;
+
+ /* Enable software gain only if max software gain is specified in UCM.*/
+ if (!aio->ucm)
+ return;
+
+ rc = ucm_get_max_software_gain(aio->ucm, input->base.name,
+ &max_software_gain);
+ if (rc)
+ return;
+
+ input->base.software_volume_needed = 1;
+ input->base.max_software_gain = max_software_gain;
+ syslog(LOG_INFO,
+ "Use software gain for %s with max %ld because it is specified"
+ " in UCM", input->base.name, max_software_gain);
+}
+
+static void set_input_default_node_gain(struct alsa_input_node *input,
+ struct alsa_io *aio)
+{
+ long default_node_gain;
+ int rc;
+
+ if (!aio->ucm)
+ return;
+
+ rc = ucm_get_default_node_gain(aio->ucm, input->base.name,
+ &default_node_gain);
+ if (rc)
+ return;
+
+ input->base.capture_gain = default_node_gain;
+}
- aio = (struct alsa_io *)callback_arg;
+static void check_auto_unplug_output_node(struct alsa_io *aio,
+ struct cras_ionode *node,
+ int plugged)
+{
+ struct cras_ionode *tmp;
+
+ if (!auto_unplug_output_node(aio))
+ return;
+
+ /* Auto unplug internal speaker if any output node has been created */
+ if (!strcmp(node->name, INTERNAL_SPEAKER) && plugged) {
+ DL_FOREACH(aio->base.nodes, tmp)
+ if (tmp->plugged && (tmp != node))
+ cras_iodev_set_node_attr(node,
+ IONODE_ATTR_PLUGGED,
+ 0);
+ } else {
+ DL_FOREACH(aio->base.nodes, tmp) {
+ if (!strcmp(tmp->name, INTERNAL_SPEAKER))
+ cras_iodev_set_node_attr(tmp,
+ IONODE_ATTR_PLUGGED,
+ !plugged);
+ }
+ }
+}
+
+/*
+ * Callback for listing mixer outputs. The mixer will call this once for each
+ * output associated with this device. Most commonly this is used to tell the
+ * device it has Headphones and Speakers.
+ */
+static struct alsa_output_node *new_output(struct alsa_io *aio,
+ struct mixer_control *cras_output,
+ const char *name)
+{
+ struct alsa_output_node *output;
+ syslog(LOG_DEBUG, "New output node for '%s'", name);
if (aio == NULL) {
syslog(LOG_ERR, "Invalid aio when listing outputs.");
- return;
+ return NULL;
}
output = (struct alsa_output_node *)calloc(1, sizeof(*output));
if (output == NULL) {
syslog(LOG_ERR, "Out of memory when listing outputs.");
- return;
+ return NULL;
}
output->base.dev = &aio->base;
output->base.idx = aio->next_ionode_index++;
+ output->base.stable_id = SuperFastHash(name,
+ strlen(name),
+ aio->base.info.stable_id);
+ output->base.stable_id_new = SuperFastHash(name,
+ strlen(name),
+ aio->base.info.stable_id_new
+ );
output->mixer_output = cras_output;
- name = get_output_node_name(aio, cras_output);
+
+ /* Volume curve. */
+ output->volume_curve = cras_card_config_get_volume_curve_for_control(
+ aio->config,
+ name ? name
+ : cras_alsa_mixer_get_control_name(cras_output));
+
strncpy(output->base.name, name, sizeof(output->base.name) - 1);
set_node_initial_state(&output->base, aio->card_type);
set_output_node_software_volume_needed(output, aio);
- /* Auto unplug internal speaker if any output node has been created */
- if (auto_unplug_output_node(aio) && !strcmp(name, INTERNAL_SPEAKER)) {
- struct cras_ionode *tmp;
- DL_FOREACH(aio->base.nodes, tmp)
- if (tmp->plugged)
- output->base.plugged = 0;
+ cras_iodev_add_node(&aio->base, &output->base);
+
+ check_auto_unplug_output_node(aio, &output->base, output->base.plugged);
+ return output;
+}
+
+static void new_output_by_mixer_control(struct mixer_control *cras_output,
+ void *callback_arg)
+{
+ struct alsa_io *aio = (struct alsa_io *)callback_arg;
+ char node_name[CRAS_IODEV_NAME_BUFFER_SIZE];
+ const char *ctl_name;
+
+ ctl_name = cras_alsa_mixer_get_control_name(cras_output);
+ if (!ctl_name)
+ return;
+
+ if (aio->card_type == ALSA_CARD_TYPE_USB) {
+ snprintf(node_name, sizeof(node_name), "%s: %s",
+ aio->base.info.name, ctl_name);
+ new_output(aio, cras_output, node_name);
+ } else {
+ new_output(aio, cras_output, ctl_name);
}
+}
- cras_iodev_add_node(&aio->base, &output->base);
+static void check_auto_unplug_input_node(struct alsa_io *aio,
+ struct cras_ionode *node,
+ int plugged)
+{
+ struct cras_ionode *tmp;
+ if (!auto_unplug_input_node(aio))
+ return;
+
+ /* Auto unplug internal mic if any input node has already
+ * been created */
+ if (!strcmp(node->name, INTERNAL_MICROPHONE) && plugged) {
+ DL_FOREACH(aio->base.nodes, tmp)
+ if (tmp->plugged && (tmp != node))
+ cras_iodev_set_node_attr(node,
+ IONODE_ATTR_PLUGGED,
+ 0);
+ } else {
+ DL_FOREACH(aio->base.nodes, tmp)
+ if (!strcmp(tmp->name, INTERNAL_MICROPHONE))
+ cras_iodev_set_node_attr(tmp,
+ IONODE_ATTR_PLUGGED,
+ !plugged);
+ }
}
-static void _new_input(struct mixer_control *cras_input,
- const char *name,
- struct alsa_io *aio)
+static struct alsa_input_node *new_input(struct alsa_io *aio,
+ struct mixer_control *cras_input, const char *name)
{
struct alsa_input_node *input;
char *mic_positions;
+ int err;
input = (struct alsa_input_node *)calloc(1, sizeof(*input));
if (input == NULL) {
syslog(LOG_ERR, "Out of memory when listing inputs.");
- return;
+ return NULL;
}
input->base.dev = &aio->base;
input->base.idx = aio->next_ionode_index++;
+ input->base.stable_id = SuperFastHash(name,
+ strlen(name),
+ aio->base.info.stable_id);
+ input->base.stable_id_new = SuperFastHash(name,
+ strlen(name),
+ aio->base.info.stable_id_new);
input->mixer_input = cras_input;
strncpy(input->base.name, name, sizeof(input->base.name) - 1);
set_node_initial_state(&input->base, aio->card_type);
-
- /* Check mic positions only for internal mic. */
- if (aio->ucm && input->base.type == CRAS_NODE_TYPE_INTERNAL_MIC) {
- mic_positions = ucm_get_mic_positions(aio->ucm);
- if (mic_positions) {
- strncpy(input->base.mic_positions, mic_positions,
- sizeof(input->base.mic_positions) - 1);
- free(mic_positions);
+ set_input_node_software_volume_needed(input, aio);
+ set_input_default_node_gain(input, aio);
+
+ if (aio->ucm) {
+ /* Check mic positions only for internal mic. */
+ if ((input->base.type == CRAS_NODE_TYPE_MIC) &&
+ (input->base.position == NODE_POSITION_INTERNAL)) {
+ mic_positions = ucm_get_mic_positions(aio->ucm);
+ if (mic_positions) {
+ strncpy(input->base.mic_positions,
+ mic_positions,
+ sizeof(input->base.mic_positions) - 1);
+ free(mic_positions);
+ }
}
- }
- /* Auto unplug internal mic if any input node has already
- * been created */
- if (auto_unplug_input_node(aio) && !strcmp(name, INTERNAL_MICROPHONE)) {
- struct cras_ionode *tmp;
- DL_FOREACH(aio->base.nodes, tmp)
- if (tmp->plugged)
- input->base.plugged = 0;
+ /* Check if channel map is specified in UCM. */
+ input->channel_layout = (int8_t *)malloc(
+ CRAS_CH_MAX * sizeof(*input->channel_layout));
+ err = ucm_get_capture_chmap_for_dev(aio->ucm, name,
+ input->channel_layout);
+ if (err) {
+ free(input->channel_layout);
+ input->channel_layout = 0;
+ }
}
cras_iodev_add_node(&aio->base, &input->base);
+ check_auto_unplug_input_node(aio, &input->base,
+ input->base.plugged);
+ return input;
}
-static void new_input(struct mixer_control *cras_input,
- void *callback_arg)
+static void new_input_by_mixer_control(struct mixer_control *cras_input,
+ void *callback_arg)
{
- struct alsa_io *aio;
- const char* name;
- aio = (struct alsa_io *)callback_arg;
- name = get_input_node_name(aio, cras_input);
- _new_input(cras_input, name, aio);
-}
-
-static void new_input_by_name(const char *name, struct alsa_io *aio)
-{
- _new_input(NULL, name, aio);
+ struct alsa_io *aio = (struct alsa_io *)callback_arg;
+ char node_name[CRAS_IODEV_NAME_BUFFER_SIZE];
+ const char *ctl_name = cras_alsa_mixer_get_control_name(cras_input);
+
+ if (aio->card_type == ALSA_CARD_TYPE_USB) {
+ snprintf(node_name , sizeof(node_name), "%s: %s",
+ aio->base.info.name, ctl_name);
+ new_input(aio, cras_input, node_name);
+ } else {
+ new_input(aio, cras_input, ctl_name);
+ }
}
-/* Finds the output node associated with the jack. Returns NULL if not found. */
+/*
+ * Finds the output node associated with the jack. Returns NULL if not found.
+ */
static struct alsa_output_node *get_output_node_from_jack(
struct alsa_io *aio, const struct cras_alsa_jack *jack)
{
@@ -935,13 +1282,16 @@ static struct alsa_output_node *get_output_node_from_jack(
struct cras_ionode *node = NULL;
struct alsa_output_node *aout = NULL;
- mixer_output = cras_alsa_jack_get_mixer_output(jack);
- if (mixer_output == NULL) {
- /* no mixer output, search by node. */
- DL_SEARCH_SCALAR_WITH_CAST(aio->base.nodes, node, aout,
- jack, jack);
+ /* Search by jack first. */
+ DL_SEARCH_SCALAR_WITH_CAST(aio->base.nodes, node, aout,
+ jack, jack);
+ if (aout)
return aout;
- }
+
+ /* Search by mixer control next. */
+ mixer_output = cras_alsa_jack_get_mixer_output(jack);
+ if (mixer_output == NULL)
+ return NULL;
DL_SEARCH_SCALAR_WITH_CAST(aio->base.nodes, node, aout,
mixer_output, mixer_output);
@@ -967,9 +1317,11 @@ static struct alsa_input_node *get_input_node_from_jack(
return ain;
}
-/* Returns the dsp name specified in the ucm config. If there is a dsp
+/*
+ * Returns the dsp name specified in the ucm config. If there is a dsp
* name specified for the jack of the active node, use that. Otherwise
- * use the default dsp name for the alsa_io device. */
+ * use the default dsp name for the alsa_io device.
+ */
static const char *get_active_dsp_name(struct alsa_io *aio)
{
struct cras_ionode *node = aio->base.active_node;
@@ -986,7 +1338,34 @@ static const char *get_active_dsp_name(struct alsa_io *aio)
return cras_alsa_jack_get_dsp_name(jack) ? : aio->dsp_name_default;
}
-/* Callback that is called when an output jack is plugged or unplugged. */
+/*
+ * Creates volume curve for the node associated with given jack.
+ */
+static struct cras_volume_curve *create_volume_curve_for_jack(
+ const struct cras_card_config *config,
+ const struct cras_alsa_jack *jack)
+{
+ struct cras_volume_curve *curve;
+ const char *name;
+
+ /* Use jack's UCM device name as key to get volume curve. */
+ name = cras_alsa_jack_get_ucm_device(jack);
+ curve = cras_card_config_get_volume_curve_for_control(config, name);
+ if (curve)
+ return curve;
+
+ /* Use alsa jack's name as key to get volume curve. */
+ name = cras_alsa_jack_get_name(jack);
+ curve = cras_card_config_get_volume_curve_for_control(config, name);
+ if (curve)
+ return curve;
+
+ return NULL;
+}
+
+/*
+ * Callback that is called when an output jack is plugged or unplugged.
+ */
static void jack_output_plug_event(const struct cras_alsa_jack *jack,
int plugged,
void *arg)
@@ -1000,37 +1379,42 @@ static void jack_output_plug_event(const struct cras_alsa_jack *jack,
aio = (struct alsa_io *)arg;
node = get_output_node_from_jack(aio, jack);
+ jack_name = cras_alsa_jack_get_name(jack);
+ if (!strcmp(jack_name, "Speaker Phantom Jack"))
+ jack_name = INTERNAL_SPEAKER;
/* If there isn't a node for this jack, create one. */
if (node == NULL) {
- node = (struct alsa_output_node *)calloc(1, sizeof(*node));
- if (node == NULL) {
- syslog(LOG_ERR, "Out of memory creating jack node.");
+ if (aio->fully_specified) {
+ /* When fully specified, can't have new nodes. */
+ syslog(LOG_ERR, "No matching output node for jack %s!",
+ jack_name);
return;
}
- node->base.dev = &aio->base;
- node->base.idx = aio->next_ionode_index++;
- jack_name = cras_alsa_jack_get_name(jack);
- node->jack_curve = cras_alsa_mixer_create_volume_curve_for_name(
- aio->mixer, jack_name);
- node->jack = jack;
- /* Speaker phantom jack is actually for internal speaker. */
- if (!strcmp(jack_name, "Speaker Phantom Jack"))
- jack_name = INTERNAL_SPEAKER;
- strncpy(node->base.name, jack_name,
- sizeof(node->base.name) - 1);
- set_node_initial_state(&node->base, aio->card_type);
- set_output_node_software_volume_needed(node, aio);
+ node = new_output(aio, NULL, jack_name);
+ if (node == NULL)
+ return;
+
cras_alsa_jack_update_node_type(jack, &(node->base.type));
- cras_iodev_add_node(&aio->base, &node->base);
- } else if (!node->jack) {
+ }
+
+ if (!node->jack) {
+ if (aio->fully_specified)
+ syslog(LOG_ERR,
+ "Jack '%s' was found to match output node '%s'."
+ " Please fix your UCM configuration to match.",
+ jack_name, node->base.name);
+
/* If we already have the node, associate with the jack. */
- jack_name = cras_alsa_jack_get_name(jack);
- node->jack_curve = cras_alsa_mixer_create_volume_curve_for_name(
- aio->mixer, jack_name);
node->jack = jack;
+ if (node->volume_curve == NULL)
+ node->volume_curve = create_volume_curve_for_jack(
+ aio->config, jack);
}
+ syslog(LOG_DEBUG, "%s plugged: %d, %s", jack_name, plugged,
+ cras_alsa_mixer_get_control_name(node->mixer_output));
+
cras_alsa_jack_update_monitor_name(jack, node->base.name,
sizeof(node->base.name));
/* The name got from jack might be an invalid UTF8 string. */
@@ -1039,66 +1423,63 @@ static void jack_output_plug_event(const struct cras_alsa_jack *jack,
cras_iodev_set_node_attr(&node->base, IONODE_ATTR_PLUGGED, plugged);
- if (auto_unplug_output_node(aio)) {
- struct cras_ionode *tmp;
- DL_FOREACH(aio->base.nodes, tmp) {
- if (!strcmp(tmp->name, INTERNAL_SPEAKER))
- cras_iodev_set_node_attr(tmp,
- IONODE_ATTR_PLUGGED,
- !plugged);
- }
- }
+ check_auto_unplug_output_node(aio, &node->base, plugged);
}
-/* Callback that is called when an input jack is plugged or unplugged. */
+/*
+ * Callback that is called when an input jack is plugged or unplugged.
+ */
static void jack_input_plug_event(const struct cras_alsa_jack *jack,
int plugged,
void *arg)
{
struct alsa_io *aio;
struct alsa_input_node *node;
+ struct mixer_control *cras_input;
const char *jack_name;
if (arg == NULL)
return;
aio = (struct alsa_io *)arg;
node = get_input_node_from_jack(aio, jack);
+ jack_name = cras_alsa_jack_get_name(jack);
/* If there isn't a node for this jack, create one. */
if (node == NULL) {
- node = (struct alsa_input_node *)calloc(1, sizeof(*node));
- if (node == NULL) {
- syslog(LOG_ERR, "Out of memory creating jack node.");
+ if (aio->fully_specified) {
+ /* When fully specified, can't have new nodes. */
+ syslog(LOG_ERR, "No matching input node for jack %s!",
+ jack_name);
return;
}
- node->base.dev = &aio->base;
- node->base.idx = aio->next_ionode_index++;
- jack_name = cras_alsa_jack_get_name(jack);
- node->jack = jack;
- node->mixer_input = cras_alsa_jack_get_mixer_input(jack);
- strncpy(node->base.name, jack_name,
- sizeof(node->base.name) - 1);
- set_node_initial_state(&node->base, aio->card_type);
- cras_iodev_add_node(&aio->base, &node->base);
- } else if (!node->jack) {
- /* If we already have the node, associate with the jack. */
+ cras_input = cras_alsa_jack_get_mixer_input(jack);
+ node = new_input(aio, cras_input, jack_name);
+ if (node == NULL)
+ return;
+ }
+
+ syslog(LOG_DEBUG, "%s plugged: %d, %s", jack_name, plugged,
+ cras_alsa_mixer_get_control_name(node->mixer_input));
+
+ /* If we already have the node, associate with the jack. */
+ if (!node->jack) {
+ if (aio->fully_specified)
+ syslog(LOG_ERR,
+ "Jack '%s' was found to match input node '%s'."
+ " Please fix your UCM configuration to match.",
+ jack_name, node->base.name);
node->jack = jack;
}
cras_iodev_set_node_attr(&node->base, IONODE_ATTR_PLUGGED, plugged);
- if (auto_unplug_input_node(aio)) {
- struct cras_ionode *tmp;
- DL_FOREACH(aio->base.nodes, tmp)
- if (!strcmp(tmp->name, INTERNAL_MICROPHONE))
- cras_iodev_set_node_attr(tmp,
- IONODE_ATTR_PLUGGED,
- !plugged);
- }
+ check_auto_unplug_input_node(aio, &node->base, plugged);
}
-/* Sets the name of the given iodev, using the name and index of the card
- * combined with the device index and direction */
+/*
+ * Sets the name of the given iodev, using the name and index of the card
+ * combined with the device index and direction.
+ */
static void set_iodev_name(struct cras_iodev *dev,
const char *card_name,
const char *dev_name,
@@ -1106,7 +1487,8 @@ static void set_iodev_name(struct cras_iodev *dev,
size_t device_index,
enum CRAS_ALSA_CARD_TYPE card_type,
size_t usb_vid,
- size_t usb_pid)
+ size_t usb_pid,
+ char *usb_serial_number)
{
snprintf(dev->info.name,
sizeof(dev->info.name),
@@ -1117,18 +1499,20 @@ static void set_iodev_name(struct cras_iodev *dev,
device_index);
dev->info.name[ARRAY_SIZE(dev->info.name) - 1] = '\0';
syslog(LOG_DEBUG, "Add device name=%s", dev->info.name);
- dev->info.stable_id = SuperFastHash(dev->info.name,
- strlen(dev->info.name),
- strlen(dev->info.name));
+
+ dev->info.stable_id = SuperFastHash(card_name,
+ strlen(card_name),
+ strlen(card_name));
+ dev->info.stable_id = SuperFastHash(dev_name,
+ strlen(dev_name),
+ dev->info.stable_id);
switch (card_type) {
case ALSA_CARD_TYPE_INTERNAL:
- dev->info.stable_id = SuperFastHash((const char *)&card_index,
- sizeof(card_index),
- dev->info.stable_id);
dev->info.stable_id = SuperFastHash((const char *)&device_index,
sizeof(device_index),
dev->info.stable_id);
+ dev->info.stable_id_new = dev->info.stable_id;
break;
case ALSA_CARD_TYPE_USB:
dev->info.stable_id = SuperFastHash((const char *)&usb_vid,
@@ -1137,16 +1521,46 @@ static void set_iodev_name(struct cras_iodev *dev,
dev->info.stable_id = SuperFastHash((const char *)&usb_pid,
sizeof(usb_pid),
dev->info.stable_id);
+ dev->info.stable_id_new =
+ SuperFastHash(usb_serial_number,
+ strlen(usb_serial_number),
+ dev->info.stable_id);
+ break;
+ default:
+ dev->info.stable_id_new = dev->info.stable_id;
break;
}
- syslog(LOG_DEBUG, "Stable ID=%08x", dev->info.stable_id);
+ syslog(LOG_DEBUG, "Stable ID=%08x, New Stable ID=%08x",
+ dev->info.stable_id, dev->info.stable_id_new);
+}
+
+static int get_fixed_rate(struct alsa_io *aio)
+{
+ const char *name;
+
+ if (aio->base.direction == CRAS_STREAM_OUTPUT) {
+ struct alsa_output_node *active = get_active_output(aio);
+ if (!active)
+ return -ENOENT;
+ name = active->base.name;
+ } else {
+ struct alsa_input_node *active = get_active_input(aio);
+ if (!active)
+ return -ENOENT;
+ name = active->base.name;
+ }
+
+ return ucm_get_sample_rate_for_dev(aio->ucm, name, aio->base.direction);
}
-/* Updates the supported sample rates and channel counts. */
+/*
+ * Updates the supported sample rates and channel counts.
+ */
static int update_supported_formats(struct cras_iodev *iodev)
{
struct alsa_io *aio = (struct alsa_io *)iodev;
int err;
+ int fixed_rate;
free(iodev->supported_rates);
iodev->supported_rates = NULL;
@@ -1159,10 +1573,26 @@ static int update_supported_formats(struct cras_iodev *iodev)
&iodev->supported_rates,
&iodev->supported_channel_counts,
&iodev->supported_formats);
- return err;
+ if (err)
+ return err;
+
+ if (aio->ucm) {
+ /* Allow UCM to override supplied rates. */
+ fixed_rate = get_fixed_rate(aio);
+ if (fixed_rate > 0) {
+ free(iodev->supported_rates);
+ iodev->supported_rates = (size_t*)malloc(
+ 2 * sizeof(iodev->supported_rates[0]));
+ iodev->supported_rates[0] = fixed_rate;
+ iodev->supported_rates[1] = 0;
+ }
+ }
+ return 0;
}
-/* Builds software volume scalers for output nodes in the device. */
+/*
+ * Builds software volume scalers for output nodes in the device.
+ */
static void build_softvol_scalers(struct alsa_io *aio)
{
struct cras_ionode *ionode;
@@ -1178,6 +1608,190 @@ static void build_softvol_scalers(struct alsa_io *aio)
}
}
+static void enable_active_ucm(struct alsa_io *aio, int plugged)
+{
+ const struct cras_alsa_jack *jack;
+ const char *name;
+
+ if (aio->base.direction == CRAS_STREAM_OUTPUT) {
+ struct alsa_output_node *active = get_active_output(aio);
+ if (!active)
+ return;
+ name = active->base.name;
+ jack = active->jack;
+ } else {
+ struct alsa_input_node *active = get_active_input(aio);
+ if (!active)
+ return;
+ name = active->base.name;
+ jack = active->jack;
+ }
+
+ if (jack)
+ cras_alsa_jack_enable_ucm(jack, plugged);
+ else if (aio->ucm)
+ ucm_set_enabled(aio->ucm, name, plugged);
+}
+
+static int fill_whole_buffer_with_zeros(struct cras_iodev *iodev)
+{
+ struct alsa_io *aio = (struct alsa_io *)iodev;
+ int rc;
+ uint8_t *dst = NULL;
+ size_t format_bytes;
+
+ /* Fill whole buffer with zeros. */
+ rc = cras_alsa_mmap_get_whole_buffer(
+ aio->handle, &dst, &aio->num_underruns);
+
+ if (rc < 0) {
+ syslog(LOG_ERR, "Failed to get whole buffer: %s",
+ snd_strerror(rc));
+ return rc;
+ }
+
+ format_bytes = cras_get_format_bytes(iodev->format);
+ memset(dst, 0, iodev->buffer_size * format_bytes);
+
+ return 0;
+}
+
+static int adjust_appl_ptr(struct cras_iodev *odev)
+{
+ struct alsa_io *aio = (struct alsa_io *)odev;
+
+ /* Move appl_ptr to min_buffer_level + min_cb_level frames ahead of
+ * hw_ptr when resuming from free run or adjusting appl_ptr from
+ * underrun. */
+ return cras_alsa_resume_appl_ptr(
+ aio->handle,
+ odev->min_buffer_level + odev->min_cb_level);
+}
+
+static int alsa_output_underrun(struct cras_iodev *odev)
+{
+ int rc;
+ /* Fill whole buffer with zeros. This avoids samples left in buffer causing
+ * noise when device plays them. */
+ rc = fill_whole_buffer_with_zeros(odev);
+ if (rc)
+ return rc;
+ /* Adjust appl_ptr to leave underrun. */
+ return adjust_appl_ptr(odev);
+}
+
+static int possibly_enter_free_run(struct cras_iodev *odev)
+{
+ struct alsa_io *aio = (struct alsa_io *)odev;
+ int rc;
+ unsigned int hw_level, fr_to_write;
+ unsigned int target_hw_level = odev->min_cb_level * 2;
+ struct timespec hw_tstamp;
+
+ if (aio->is_free_running)
+ return 0;
+
+ /* Check if all valid samples are played.
+ * If all valid samples are played, fill whole buffer with zeros. */
+ rc = cras_iodev_frames_queued(odev, &hw_tstamp);
+ if (rc < 0)
+ return rc;
+ hw_level = rc;
+
+ if (hw_level < aio->filled_zeros_for_draining || hw_level == 0) {
+ rc = fill_whole_buffer_with_zeros(odev);
+ if (rc < 0)
+ return rc;
+ aio->is_free_running = 1;
+ return 0;
+ }
+
+ /* Fill some zeros to drain valid samples. */
+ fr_to_write = cras_iodev_buffer_avail(odev, hw_level);
+
+ if (hw_level <= target_hw_level) {
+ fr_to_write = MIN(target_hw_level - hw_level, fr_to_write);
+ rc = cras_iodev_fill_odev_zeros(odev, fr_to_write);
+ if (rc)
+ return rc;
+ aio->filled_zeros_for_draining += fr_to_write;
+ }
+
+ return 0;
+}
+
+static int leave_free_run(struct cras_iodev *odev)
+{
+ struct alsa_io *aio = (struct alsa_io *)odev;
+ int rc;
+
+ if (!aio->is_free_running)
+ return 0;
+
+ rc = adjust_appl_ptr(odev);
+ if (rc) {
+ syslog(LOG_ERR, "device %s failed to leave free run, rc = %d",
+ odev->info.name, rc);
+ return rc;
+ }
+ aio->is_free_running = 0;
+ aio->filled_zeros_for_draining = 0;
+
+ return 0;
+}
+
+/*
+ * Free run state is the optimization of no_stream playback on alsa_io.
+ * The whole buffer will be filled with zeros. Device can play these zeros
+ * indefinitely. When there is new meaningful sample, appl_ptr should be
+ * resumed to some distance ahead of hw_ptr.
+ */
+static int no_stream(struct cras_iodev *odev, int enable)
+{
+ if (enable)
+ return possibly_enter_free_run(odev);
+ else
+ return leave_free_run(odev);
+}
+
+static int output_should_wake(const struct cras_iodev *odev)
+{
+ struct alsa_io *aio = (struct alsa_io *)odev;
+ if (aio->is_free_running)
+ return 0;
+ else
+ return ((cras_iodev_state(odev) ==
+ CRAS_IODEV_STATE_NO_STREAM_RUN) ||
+ (cras_iodev_state(odev) ==
+ CRAS_IODEV_STATE_NORMAL_RUN));
+}
+
+static unsigned int get_num_underruns(const struct cras_iodev *iodev)
+{
+ const struct alsa_io *aio = (const struct alsa_io *)iodev;
+ return aio->num_underruns;
+}
+
+static unsigned int get_num_severe_underruns(const struct cras_iodev *iodev)
+{
+ const struct alsa_io *aio = (const struct alsa_io *)iodev;
+ return aio->num_severe_underruns;
+}
+
+static void set_default_hotword_model(struct cras_iodev *iodev)
+{
+ const char *default_model = "en_us";
+ cras_node_id_t node_id;
+
+ if (!iodev->active_node ||
+ iodev->active_node->type != CRAS_NODE_TYPE_HOTWORD)
+ return;
+
+ node_id = cras_make_node_id(iodev->info.idx, iodev->active_node->idx);
+ /* This is a no-op if the default_model is not supported */
+ cras_iodev_list_set_hotword_model(node_id, default_model);
+}
+
/*
* Exported Interface.
*/
@@ -1190,14 +1804,16 @@ struct cras_iodev *alsa_iodev_create(size_t card_index,
enum CRAS_ALSA_CARD_TYPE card_type,
int is_first,
struct cras_alsa_mixer *mixer,
- snd_use_case_mgr_t *ucm,
+ const struct cras_card_config *config,
+ struct cras_use_case_mgr *ucm,
+ snd_hctl_t *hctl,
enum CRAS_STREAM_DIRECTION direction,
size_t usb_vid,
- size_t usb_pid)
+ size_t usb_pid,
+ char *usb_serial_number)
{
struct alsa_io *aio;
struct cras_iodev *iodev;
- int err;
if (direction != CRAS_STREAM_INPUT && direction != CRAS_STREAM_OUTPUT)
return NULL;
@@ -1212,6 +1828,19 @@ struct cras_iodev *alsa_iodev_create(size_t card_index,
aio->card_type = card_type;
aio->is_first = is_first;
aio->handle = NULL;
+ aio->num_severe_underruns = 0;
+ if (dev_name) {
+ aio->dev_name = strdup(dev_name);
+ if (!aio->dev_name)
+ goto cleanup_iodev;
+ }
+ if (dev_id) {
+ aio->dev_id = strdup(dev_id);
+ if (!aio->dev_id)
+ goto cleanup_iodev;
+ }
+ aio->is_free_running = 0;
+ aio->filled_zeros_for_draining = 0;
aio->dev = (char *)malloc(MAX_ALSA_DEV_NAME_LENGTH);
if (aio->dev == NULL)
goto cleanup_iodev;
@@ -1228,35 +1857,45 @@ struct cras_iodev *alsa_iodev_create(size_t card_index,
} else {
aio->alsa_stream = SND_PCM_STREAM_PLAYBACK;
aio->base.set_volume = set_alsa_volume;
- aio->base.set_mute = set_alsa_volume;
+ aio->base.set_mute = set_alsa_mute;
+ aio->base.output_underrun = alsa_output_underrun;
}
iodev->open_dev = open_dev;
iodev->close_dev = close_dev;
- iodev->is_open = is_open;
iodev->update_supported_formats = update_supported_formats;
iodev->frames_queued = frames_queued;
iodev->delay_frames = delay_frames;
iodev->get_buffer = get_buffer;
iodev->put_buffer = put_buffer;
iodev->flush_buffer = flush_buffer;
- iodev->dev_running = dev_running;
+ iodev->start = start;
iodev->update_active_node = update_active_node;
iodev->update_channel_layout = update_channel_layout;
+ iodev->set_hotword_model = set_hotword_model;
+ iodev->get_hotword_models = get_hotword_models;
+ iodev->no_stream = no_stream;
+ iodev->output_should_wake = output_should_wake;
+ iodev->get_num_underruns = get_num_underruns;
+ iodev->get_num_severe_underruns = get_num_severe_underruns;
+ iodev->set_swap_mode_for_node = cras_iodev_dsp_set_swap_mode_for_node;
+
if (card_type == ALSA_CARD_TYPE_USB)
iodev->min_buffer_level = USB_EXTRA_BUFFER_FRAMES;
- err = cras_alsa_fill_properties(aio->dev, aio->alsa_stream,
- &iodev->supported_rates,
- &iodev->supported_channel_counts,
- &iodev->supported_formats);
- if (err < 0 || iodev->supported_rates[0] == 0 ||
- iodev->supported_channel_counts[0] == 0 ||
- iodev->supported_formats[0] == 0) {
- syslog(LOG_ERR, "cras_alsa_fill_properties: %s", strerror(err));
+ iodev->ramp = cras_ramp_create();
+ if (iodev->ramp == NULL)
goto cleanup_iodev;
- }
aio->mixer = mixer;
+ aio->config = config;
+ if (direction == CRAS_STREAM_OUTPUT) {
+ aio->default_volume_curve =
+ cras_card_config_get_volume_curve_for_control(
+ config, "Default");
+ if (aio->default_volume_curve == NULL)
+ aio->default_volume_curve =
+ cras_volume_curve_create_default();
+ }
aio->ucm = ucm;
if (ucm) {
unsigned int level;
@@ -1272,30 +1911,79 @@ struct cras_iodev *alsa_iodev_create(size_t card_index,
level = ucm_get_min_buffer_level(ucm);
if (level && direction == CRAS_STREAM_OUTPUT)
iodev->min_buffer_level = level;
- }
- set_iodev_name(iodev, card_name, dev_name, card_index, device_index,
- card_type, usb_vid, usb_pid);
- /* Create output nodes for mixer controls, such as Headphone
- * and Speaker, only for the first device. */
- if (direction == CRAS_STREAM_OUTPUT && is_first)
- cras_alsa_mixer_list_outputs(mixer, new_output, aio);
- else if (direction == CRAS_STREAM_INPUT && is_first)
- cras_alsa_mixer_list_inputs(mixer, new_input, aio);
+ aio->enable_htimestamp =
+ ucm_get_enable_htimestamp_flag(ucm);
+ }
- /* Find any jack controls for this device. */
- aio->jack_list = cras_alsa_jack_list_create(
+ set_iodev_name(iodev, card_name, dev_name, card_index, device_index,
+ card_type, usb_vid, usb_pid, usb_serial_number);
+
+ aio->jack_list =
+ cras_alsa_jack_list_create(
card_index,
card_name,
device_index,
is_first,
mixer,
ucm,
+ hctl,
direction,
direction == CRAS_STREAM_OUTPUT ?
jack_output_plug_event :
jack_input_plug_event,
aio);
+ if (!aio->jack_list)
+ goto cleanup_iodev;
+
+ /* HDMI outputs don't have volume adjustment, do it in software. */
+ if (direction == CRAS_STREAM_OUTPUT && strstr(dev_name, HDMI))
+ iodev->software_volume_needed = 1;
+
+ /* Add this now so that cleanup of the iodev (in case of error or card
+ * card removal will function as expected. */
+ if (direction == CRAS_STREAM_OUTPUT)
+ cras_iodev_list_add_output(&aio->base);
+ else
+ cras_iodev_list_add_input(&aio->base);
+ return &aio->base;
+
+cleanup_iodev:
+ free_alsa_iodev_resources(aio);
+ free(aio);
+ return NULL;
+}
+
+int alsa_iodev_legacy_complete_init(struct cras_iodev *iodev)
+{
+ struct alsa_io *aio = (struct alsa_io *)iodev;
+ const char *dev_name;
+ const char *dev_id;
+ enum CRAS_STREAM_DIRECTION direction;
+ int err;
+ int is_first;
+ struct cras_alsa_mixer *mixer;
+
+ if (!aio)
+ return -EINVAL;
+ direction = iodev->direction;
+ dev_name = aio->dev_name;
+ dev_id = aio->dev_id;
+ is_first = aio->is_first;
+ mixer = aio->mixer;
+
+ /* Create output nodes for mixer controls, such as Headphone
+ * and Speaker, only for the first device. */
+ if (direction == CRAS_STREAM_OUTPUT && is_first)
+ cras_alsa_mixer_list_outputs(mixer,
+ new_output_by_mixer_control, aio);
+ else if (direction == CRAS_STREAM_INPUT && is_first)
+ cras_alsa_mixer_list_inputs(mixer,
+ new_input_by_mixer_control, aio);
+
+ err = cras_alsa_jack_list_find_jacks_by_name_matching(aio->jack_list);
+ if (err)
+ return err;
/* Create nodes for jacks that aren't associated with an
* already existing node. Get an initial read of the jacks for
@@ -1309,50 +1997,131 @@ struct cras_iodev *alsa_iodev_create(size_t card_index,
* which really don't have an internal device. */
if ((direction == CRAS_STREAM_OUTPUT) &&
!no_create_default_output_node(aio)) {
- if (!aio->base.nodes || (first_internal_device(aio) &&
- !has_node(aio, INTERNAL_SPEAKER) &&
- !has_node(aio, HDMI)))
- new_output(NULL, aio);
+ if (first_internal_device(aio) &&
+ !has_node(aio, INTERNAL_SPEAKER) &&
+ !has_node(aio, HDMI)) {
+ if (strstr(aio->base.info.name, HDMI))
+ new_output(aio, NULL, HDMI);
+ else
+ new_output(aio, NULL, INTERNAL_SPEAKER);
+ } else if (!aio->base.nodes) {
+ new_output(aio, NULL, DEFAULT);
+ }
} else if ((direction == CRAS_STREAM_INPUT) &&
!no_create_default_input_node(aio)) {
if (first_internal_device(aio) &&
!has_node(aio, INTERNAL_MICROPHONE))
- new_input_by_name(INTERNAL_MICROPHONE, aio);
+ new_input(aio, NULL, INTERNAL_MICROPHONE);
else if (strstr(dev_name, KEYBOARD_MIC))
- new_input_by_name(KEYBOARD_MIC, aio);
- else if (dev_id && strstr(dev_id, AOKR_DEV))
- new_input_by_name(AOKR_DEV, aio);
+ new_input(aio, NULL, KEYBOARD_MIC);
+ else if (dev_id && strstr(dev_id, HOTWORD_DEV))
+ new_input(aio, NULL, HOTWORD_DEV);
else if (!aio->base.nodes)
- new_input_by_name(DEFAULT, aio);
+ new_input(aio, NULL, DEFAULT);
}
- /* HDMI outputs don't have volume adjustment, do it in software. */
- if (direction == CRAS_STREAM_OUTPUT && strstr(dev_name, HDMI))
- iodev->software_volume_needed = 1;
-
/* Build software volume scalers. */
if (direction == CRAS_STREAM_OUTPUT)
build_softvol_scalers(aio);
/* Set the active node as the best node we have now. */
alsa_iodev_set_active_node(&aio->base,
- first_plugged_node(&aio->base));
- if (direction == CRAS_STREAM_OUTPUT)
- cras_iodev_list_add_output(&aio->base);
- else
- cras_iodev_list_add_input(&aio->base);
+ first_plugged_node(&aio->base),
+ 0);
/* Set plugged for the first USB device per card when it appears. */
- if (card_type == ALSA_CARD_TYPE_USB && is_first)
+ if (aio->card_type == ALSA_CARD_TYPE_USB && is_first)
cras_iodev_set_node_attr(iodev->active_node,
IONODE_ATTR_PLUGGED, 1);
- return &aio->base;
+ set_default_hotword_model(iodev);
-cleanup_iodev:
- free_alsa_iodev_resources(aio);
- free(aio);
- return NULL;
+ return 0;
+}
+
+int alsa_iodev_ucm_add_nodes_and_jacks(struct cras_iodev *iodev,
+ struct ucm_section *section)
+{
+ struct alsa_io *aio = (struct alsa_io *)iodev;
+ struct mixer_control *control;
+ struct alsa_input_node *input_node = NULL;
+ struct cras_alsa_jack *jack;
+ struct alsa_output_node *output_node = NULL;
+ int rc;
+
+ if (!aio || !section)
+ return -EINVAL;
+ if ((uint32_t)section->dev_idx != aio->device_index)
+ return -EINVAL;
+
+ /* This iodev is fully specified. Avoid automatic node creation. */
+ aio->fully_specified = 1;
+
+ /* Check here in case the DmaPeriodMicrosecs flag has only been
+ * specified on one of many device entries with the same PCM. */
+ if (!aio->dma_period_set_microsecs)
+ aio->dma_period_set_microsecs =
+ ucm_get_dma_period_for_dev(aio->ucm, section->name);
+
+ /* Create a node matching this section. If there is a matching
+ * control use that, otherwise make a node without a control. */
+ control = cras_alsa_mixer_get_control_for_section(aio->mixer, section);
+ if (iodev->direction == CRAS_STREAM_OUTPUT) {
+ output_node = new_output(aio, control, section->name);
+ if (!output_node)
+ return -ENOMEM;
+ } else if (iodev->direction == CRAS_STREAM_INPUT) {
+ input_node = new_input(aio, control, section->name);
+ if (!input_node)
+ return -ENOMEM;
+ }
+
+ /* Find any jack controls for this device. */
+ rc = cras_alsa_jack_list_add_jack_for_section(
+ aio->jack_list, section, &jack);
+ if (rc)
+ return rc;
+
+ /* Associated the jack with the node. */
+ if (jack) {
+ if (output_node) {
+ output_node->jack = jack;
+ if (!output_node->volume_curve)
+ output_node->volume_curve =
+ create_volume_curve_for_jack(
+ aio->config, jack);
+ } else if (input_node) {
+ input_node->jack = jack;
+ }
+ }
+ return 0;
+}
+
+void alsa_iodev_ucm_complete_init(struct cras_iodev *iodev)
+{
+ struct alsa_io *aio = (struct alsa_io *)iodev;
+
+ if (!iodev)
+ return;
+
+ /* Get an initial read of the jacks for this device. */
+ cras_alsa_jack_list_report(aio->jack_list);
+
+ /* Build software volume scaler. */
+ if (iodev->direction == CRAS_STREAM_OUTPUT)
+ build_softvol_scalers(aio);
+
+ /* Set the active node as the best node we have now. */
+ alsa_iodev_set_active_node(&aio->base,
+ first_plugged_node(&aio->base),
+ 0);
+
+ /* Set plugged for the first USB device per card when it appears. */
+ if (aio->card_type == ALSA_CARD_TYPE_USB && aio->is_first)
+ cras_iodev_set_node_attr(iodev->active_node,
+ IONODE_ATTR_PLUGGED, 1);
+
+ set_default_hotword_model(iodev);
}
void alsa_iodev_destroy(struct cras_iodev *iodev)
@@ -1373,9 +2142,22 @@ void alsa_iodev_destroy(struct cras_iodev *iodev)
/* Free resources when device successfully removed. */
free_alsa_iodev_resources(aio);
+ cras_volume_curve_destroy(aio->default_volume_curve);
free(iodev);
}
+unsigned alsa_iodev_index(struct cras_iodev *iodev)
+{
+ struct alsa_io *aio = (struct alsa_io *)iodev;
+ return aio->device_index;
+}
+
+int alsa_iodev_has_hctl_jacks(struct cras_iodev *iodev)
+{
+ struct alsa_io *aio = (struct alsa_io *)iodev;
+ return cras_alsa_jack_list_has_hctl_jacks(aio->jack_list);
+}
+
static void alsa_iodev_unmute_node(struct alsa_io *aio,
struct cras_ionode *ionode)
{
@@ -1388,7 +2170,7 @@ static void alsa_iodev_unmute_node(struct alsa_io *aio,
* active mixer output and mute all others, otherwise just set
* the node as active and set the volume curve. */
if (mixer) {
- set_alsa_mute(aio, 1);
+ set_alsa_mute_control(aio, 1);
/* Unmute the active mixer output, mute all others. */
DL_FOREACH(aio->base.nodes, node) {
output = (struct alsa_output_node *)node;
@@ -1399,35 +2181,27 @@ static void alsa_iodev_unmute_node(struct alsa_io *aio,
}
}
-static void enable_jack_ucm(struct alsa_io *aio, int plugged)
-{
- if (aio->base.direction == CRAS_STREAM_OUTPUT) {
- struct alsa_output_node *active = get_active_output(aio);
- if (active)
- cras_alsa_jack_enable_ucm(active->jack, plugged);
- } else {
- struct alsa_input_node *active = get_active_input(aio);
- if (active)
- cras_alsa_jack_enable_ucm(active->jack, plugged);
- }
-}
-
-int alsa_iodev_set_active_node(struct cras_iodev *iodev,
- struct cras_ionode *ionode)
+static int alsa_iodev_set_active_node(struct cras_iodev *iodev,
+ struct cras_ionode *ionode,
+ unsigned dev_enabled)
{
struct alsa_io *aio = (struct alsa_io *)iodev;
- if (iodev->active_node == ionode)
+ if (iodev->active_node == ionode) {
+ enable_active_ucm(aio, dev_enabled);
+ init_device_settings(aio);
return 0;
+ }
- enable_jack_ucm(aio, 0);
- if (iodev->direction == CRAS_STREAM_OUTPUT)
+ /* Disable jack ucm before switching node. */
+ enable_active_ucm(aio, 0);
+ if (dev_enabled && (iodev->direction == CRAS_STREAM_OUTPUT))
alsa_iodev_unmute_node(aio, ionode);
cras_iodev_set_active_node(iodev, ionode);
aio->base.dsp_name = get_active_dsp_name(aio);
cras_iodev_update_dsp(iodev);
- enable_jack_ucm(aio, 1);
+ enable_active_ucm(aio, dev_enabled);
/* Setting the volume will also unmute if the system isn't muted. */
init_device_settings(aio);
return 0;
diff --git a/cras/src/server/cras_alsa_io.h b/cras/src/server/cras_alsa_io.h
index 561516f4..0b3e5482 100644
--- a/cras/src/server/cras_alsa_io.h
+++ b/cras/src/server/cras_alsa_io.h
@@ -7,12 +7,14 @@
#define CRAS_ALSA_IO_H_
#include <alsa/asoundlib.h>
-#include <alsa/use-case.h>
+#include "cras_card_config.h"
#include "cras_types.h"
struct cras_alsa_mixer;
struct cras_ionode;
+struct cras_use_case_mgr;
+struct ucm_section;
/* Initializes an alsa iodev.
* Args:
@@ -24,10 +26,13 @@ struct cras_ionode;
* card_type - the type of the card this iodev belongs.
* is_first - if this is the first iodev on the card.
* mixer - The mixer for the alsa device.
- * ucm - ALSA use case manager if available.
- * direciton - input or output.
+ * config - Card config for this alsa device.
+ * ucm - CRAS use case manager if available.
+ * hctl - high-level control manager if available.
+ * direction - input or output.
* usb_vid - vendor ID of USB device.
* usb_pid - product ID of USB device.
+ * usb_serial_number - serial number of USB device.
* Returns:
* A pointer to the newly created iodev if successful, NULL otherwise.
*/
@@ -39,30 +44,50 @@ struct cras_iodev *alsa_iodev_create(size_t card_index,
enum CRAS_ALSA_CARD_TYPE card_type,
int is_first,
struct cras_alsa_mixer *mixer,
- snd_use_case_mgr_t *ucm,
+ const struct cras_card_config *config,
+ struct cras_use_case_mgr *ucm,
+ snd_hctl_t *hctl,
enum CRAS_STREAM_DIRECTION direction,
size_t usb_vid,
- size_t usb_pid);
+ size_t usb_pid,
+ char *usb_serial_number);
-/* Destroys an alsa_iodev created with alsa_iodev_create. */
-void alsa_iodev_destroy(struct cras_iodev *iodev);
+/* Complete initializeation of this iodev with the legacy method.
+ * Add IO nodes and find jacks for this iodev with magic sauce, then choose
+ * the current active node.
+ * Args:
+ * iodev - ALSA io device associated with the IO nodes.
+ * section - UCM section information if available (or NULL).
+ * Returns:
+ * 0 for success, negative error code on error.
+ */
+int alsa_iodev_legacy_complete_init(struct cras_iodev *iodev);
-/* Sets the active node of an alsa mixer. Used to switch form Speaker to
- * Headphones or vice-versa.
+/* Add IO nodes and jacks for this iodev using UCM data.
* Args:
- * iodev - An iodev created with alsa_iodev_create.
- * ionode - The node to activate.
+ * iodev - ALSA io device associated with the given section.
+ * section - UCM section information.
+ * Returns:
+ * 0 for success, negative error code on error.
*/
-int alsa_iodev_set_active_node(struct cras_iodev *iodev,
- struct cras_ionode *ionode);
+int alsa_iodev_ucm_add_nodes_and_jacks(struct cras_iodev *iodev,
+ struct ucm_section *section);
-/* Sets the active input of an alsa mixer. Used to switch between different
- * Microphones.
+/* Complete initialization of this iodev with fully-spec UCM data.
+ * After all UCM devices associated with the same iodev have been processed
+ * this is called to finish iodev setup.
* Args:
- * iodev - An iodev created with alsa_iodev_create.
- * ionode - The input to activate.
+ * iodev - ALSA io device.
*/
-int alsa_iodev_set_active_input(struct cras_iodev *iodev,
- struct cras_ionode *ionode);
+void alsa_iodev_ucm_complete_init(struct cras_iodev *iodev);
+
+/* Destroys an alsa_iodev created with alsa_iodev_create. */
+void alsa_iodev_destroy(struct cras_iodev *iodev);
+
+/* Returns the ALSA device index for the given ALSA iodev. */
+unsigned alsa_iodev_index(struct cras_iodev *iodev);
+
+/* Returns whether this IODEV has ALSA hctl jacks. */
+int alsa_iodev_has_hctl_jacks(struct cras_iodev *iodev);
#endif /* CRAS_ALSA_IO_H_ */
diff --git a/cras/src/server/cras_alsa_jack.c b/cras/src/server/cras_alsa_jack.c
index 6f4275e1..2f69e3b2 100644
--- a/cras/src/server/cras_alsa_jack.c
+++ b/cras/src/server/cras_alsa_jack.c
@@ -5,6 +5,7 @@
#include <alsa/asoundlib.h>
#include <linux/input.h>
+#include <regex.h>
#include <syslog.h>
#include "cras_alsa_jack.h"
@@ -19,6 +20,7 @@
static const unsigned int DISPLAY_INFO_RETRY_DELAY_MS = 200;
static const unsigned int DISPLAY_INFO_MAX_RETRIES = 10;
+static const unsigned int DISPLAY_INFO_GPIO_MAX_RETRIES = 25;
/* Constants used to retrieve monitor name from ELD buffer. */
static const unsigned int ELD_MNL_MASK = 31;
@@ -96,12 +98,15 @@ struct cras_alsa_jack {
};
/* Contains all Jacks for a given device.
- * hctl - alsa hcontrol for this device.
+ * hctl - alsa hcontrol for this device's card
+ * - not opened by the jack list.
* mixer - cras mixer for the card providing this device.
+ * ucm - CRAS use case manager if available.
+ * card_index - Index ALSA uses to refer to the card. The X in "hw:X".
+ * card_name - The name of the card.
* device_index - Index ALSA uses to refer to the device. The Y in "hw:X,Y".
* is_first_device - whether this device is the first device on the card.
- * registered_fds - list of fds registered with system, to be removed upon
- * destruction.
+ * direction - Input or output.
* change_callback - function to call when the state of a jack changes.
* callback_data - data to pass back to the callback.
* jacks - list of jacks for this device.
@@ -109,15 +114,30 @@ struct cras_alsa_jack {
struct cras_alsa_jack_list {
snd_hctl_t *hctl;
struct cras_alsa_mixer *mixer;
- snd_use_case_mgr_t *ucm;
+ struct cras_use_case_mgr *ucm;
+ unsigned int card_index;
+ const char *card_name;
size_t device_index;
int is_first_device;
- struct jack_poll_fd *registered_fds;
+ enum CRAS_STREAM_DIRECTION direction;
jack_state_change_callback *change_callback;
void *callback_data;
struct cras_alsa_jack *jacks;
};
+/* Used to contain information needed while looking through GPIO jacks.
+ * jack_list - The current jack_list.
+ * section - An associated UCM section.
+ * result_jack - The resulting jack.
+ * rc - The return code for the operation.
+ */
+struct gpio_switch_list_data {
+ struct cras_alsa_jack_list *jack_list;
+ struct ucm_section *section;
+ struct cras_alsa_jack *result_jack;
+ int rc;
+};
+
/*
* Local Helpers.
*/
@@ -130,7 +150,7 @@ struct cras_alsa_jack_list {
#define LONG(x) ((x) / BITS_PER_LONG)
#define IS_BIT_SET(bit, array) !!((array[LONG(bit)]) & (1UL << OFF(bit)))
-static unsigned sys_input_get_switch_state(int fd, unsigned sw, unsigned *state)
+static int sys_input_get_switch_state(int fd, unsigned sw, unsigned *state)
{
unsigned long bits[NBITS(SW_CNT)];
const unsigned long switch_no = sw;
@@ -158,6 +178,39 @@ static inline struct cras_alsa_jack *cras_alloc_jack(int is_gpio)
return jack;
}
+static void cras_free_jack(struct cras_alsa_jack *jack,
+ int rm_select_fd)
+{
+ if (!jack)
+ return;
+
+ free(jack->ucm_device);
+ free((void *)jack->edid_file);
+ if (jack->display_info_timer)
+ cras_tm_cancel_timer(cras_system_state_get_tm(),
+ jack->display_info_timer);
+
+ if (jack->is_gpio) {
+ free(jack->gpio.device_name);
+ if (jack->gpio.fd >= 0) {
+ if (rm_select_fd)
+ cras_system_rm_select_fd(jack->gpio.fd);
+ close(jack->gpio.fd);
+ }
+ }
+
+ /*
+ * Remove the jack callback set on hctl. Otherwise, snd_hctl_close will
+ * trigger a callback while iodev might already be destroyed.
+ */
+ if (!jack->is_gpio && jack->elem)
+ snd_hctl_elem_set_callback(jack->elem, NULL);
+
+ free((void *)jack->override_type_name);
+ free((void *)jack->dsp_name);
+ free(jack);
+}
+
/* Gets the current plug state of the jack */
static int get_jack_current_state(struct cras_alsa_jack *jack)
{
@@ -251,8 +304,11 @@ static inline void jack_state_change_cb(struct cras_alsa_jack *jack, int retry)
cras_tm_cancel_timer(tm, jack->display_info_timer);
jack->display_info_timer = NULL;
}
- if (retry)
- jack->display_info_retries = DISPLAY_INFO_MAX_RETRIES;
+ if (retry) {
+ jack->display_info_retries =
+ jack->is_gpio ? DISPLAY_INFO_GPIO_MAX_RETRIES
+ : DISPLAY_INFO_MAX_RETRIES;
+ }
if (!get_jack_current_state(jack))
goto report_jack_state;
@@ -295,8 +351,8 @@ report_jack_state:
static void gpio_switch_initial_state(struct cras_alsa_jack *jack)
{
unsigned v;
- unsigned r = sys_input_get_switch_state(jack->gpio.fd,
- jack->gpio.switch_event, &v);
+ int r = sys_input_get_switch_state(jack->gpio.fd,
+ jack->gpio.switch_event, &v);
jack->gpio.current_state = r == 0 ? v : 0;
jack_state_change_cb(jack, 1);
}
@@ -387,46 +443,118 @@ static unsigned int gpio_jack_match_device(const struct cras_alsa_jack *jack,
return rc;
}
-
-/* open_and_monitor_gpio:
- *
- * Opens a /dev/input/event file associated with a headphone /
- * microphone jack and watches it for activity.
- * Returns 0 when a jack has been successfully added.
- */
-static int open_and_monitor_gpio(struct cras_alsa_jack_list *jack_list,
- enum CRAS_STREAM_DIRECTION direction,
- const char *card_name,
- const char *pathname,
- unsigned switch_event)
+static int create_jack_for_gpio(struct cras_alsa_jack_list *jack_list,
+ const char *pathname,
+ const char *dev_name,
+ unsigned switch_event,
+ struct cras_alsa_jack **out_jack)
{
struct cras_alsa_jack *jack;
unsigned long bits[NBITS(SW_CNT)];
+ const char *card_name = jack_list->card_name;
int r;
+ if (!out_jack)
+ return -EINVAL;
+ *out_jack = NULL;
+
jack = cras_alloc_jack(1);
if (jack == NULL)
return -ENOMEM;
jack->gpio.fd = gpio_switch_open(pathname);
if (jack->gpio.fd == -1) {
- free(jack);
- return -EIO;
+ r = -EIO;
+ goto error;
}
jack->gpio.switch_event = switch_event;
jack->jack_list = jack_list;
- jack->gpio.device_name = sys_input_get_device_name(pathname);
+ jack->gpio.device_name = strdup(dev_name);
+ if (!jack->gpio.device_name) {
+ r = -ENOMEM;
+ goto error;
+ }
- if (!jack->gpio.device_name ||
- !strstr(jack->gpio.device_name, card_name) ||
+ if (!strstr(jack->gpio.device_name, card_name) ||
(gpio_switch_eviocgbit(jack->gpio.fd, bits, sizeof(bits)) < 0) ||
!IS_BIT_SET(switch_event, bits)) {
- close(jack->gpio.fd);
- free(jack->gpio.device_name);
- free(jack);
+ r = -EIO;
+ goto error;
+ }
+
+ *out_jack = jack;
+ return 0;
+
+error:
+ /* Not yet registered with system select. */
+ cras_free_jack(jack, 0);
+ return r;
+}
+
+/* Take ownership and finish setup of the jack.
+ * Add the jack to the jack_list if everything goes well, or destroy it.
+ */
+static int cras_complete_gpio_jack(struct gpio_switch_list_data *data,
+ struct cras_alsa_jack *jack,
+ unsigned switch_event)
+{
+ struct cras_alsa_jack_list *jack_list = data->jack_list;
+ enum CRAS_STREAM_DIRECTION direction = jack_list->direction;
+ int r;
+
+ if (jack->ucm_device) {
+ jack->edid_file = ucm_get_edid_file_for_dev(jack_list->ucm,
+ jack->ucm_device);
+ jack->dsp_name = ucm_get_dsp_name(
+ jack->jack_list->ucm, jack->ucm_device, direction);
+ }
+
+ r = sys_input_get_switch_state(jack->gpio.fd, switch_event,
+ &jack->gpio.current_state);
+ if (r < 0) {
+ cras_free_jack(jack, 0);
return -EIO;
}
+ r = cras_system_add_select_fd(jack->gpio.fd,
+ gpio_switch_callback, jack);
+ if (r < 0) {
+ /* Not yet registered with system select. */
+ cras_free_jack(jack, 0);
+ return r;
+ }
+
+ DL_APPEND(jack_list->jacks, jack);
+ if (!data->result_jack)
+ data->result_jack = jack;
+ else if (data->section)
+ syslog(LOG_ERR,
+ "More than one jack for SectionDevice '%s'.",
+ data->section->name);
+ return 0;
+}
+
+/* open_and_monitor_gpio:
+ *
+ * Opens a /dev/input/event file associated with a headphone /
+ * microphone jack and watches it for activity.
+ * Returns 0 when a jack has been successfully added.
+ */
+static int open_and_monitor_gpio(struct gpio_switch_list_data *data,
+ const char *pathname,
+ const char *dev_name,
+ unsigned switch_event)
+{
+ struct cras_alsa_jack *jack;
+ struct cras_alsa_jack_list *jack_list = data->jack_list;
+ const char *card_name = jack_list->card_name;
+ enum CRAS_STREAM_DIRECTION direction = jack_list->direction;
+ int r;
+
+ r = create_jack_for_gpio(jack_list, pathname, dev_name,
+ switch_event, &jack);
+ if (r != 0)
+ return r;
if (jack_list->ucm)
jack->ucm_device =
@@ -435,15 +563,10 @@ static int open_and_monitor_gpio(struct cras_alsa_jack_list *jack_list,
direction);
if (!gpio_jack_match_device(jack, jack_list, card_name, direction)) {
- close(jack->gpio.fd);
- free(jack->gpio.device_name);
- free(jack);
+ cras_free_jack(jack, 0);
return -EIO;
}
-
- DL_APPEND(jack_list->jacks, jack);
-
if (direction == CRAS_STREAM_OUTPUT &&
(strstr(jack->gpio.device_name, "Headphone") ||
strstr(jack->gpio.device_name, "Headset")))
@@ -456,10 +579,6 @@ static int open_and_monitor_gpio(struct cras_alsa_jack_list *jack_list,
jack_list->mixer,
"HDMI");
- if (jack->ucm_device)
- jack->edid_file = ucm_get_edid_file_for_dev(jack_list->ucm,
- jack->ucm_device);
-
if (jack->ucm_device && direction == CRAS_STREAM_INPUT) {
char *control_name;
control_name = ucm_get_cap_control(jack->jack_list->ucm,
@@ -471,100 +590,202 @@ static int open_and_monitor_gpio(struct cras_alsa_jack_list *jack_list,
control_name);
}
- if (jack->ucm_device) {
- jack->dsp_name = ucm_get_dsp_name(
- jack->jack_list->ucm, jack->ucm_device, direction);
- }
-
- sys_input_get_switch_state(jack->gpio.fd, switch_event,
- &jack->gpio.current_state);
- r = cras_system_add_select_fd(jack->gpio.fd,
- gpio_switch_callback, jack);
- return r;
+ return cras_complete_gpio_jack(data, jack, switch_event);
}
-static int wait_for_dev_input_access()
+static int open_and_monitor_gpio_with_section(
+ struct gpio_switch_list_data *data,
+ const char *pathname,
+ unsigned switch_event)
{
- /* Wait for /dev/input/event* files to become accessible by
- * having group 'input'. Setting these files to have 'rw'
- * access to group 'input' is done through a udev rule
- * installed by adhd into /lib/udev/rules.d.
- *
- * Wait for up to 2 seconds for the /dev/input/event* files to be
- * readable by gavd.
- *
- * TODO(thutt): This could also be done with a udev enumerate
- * and then a udev monitor.
- */
- const unsigned max_iterations = 4;
- unsigned i = 0;
-
- while (i < max_iterations) {
- int readable;
- struct timeval timeout;
- const char * const pathname = "/dev/input/event0";
-
- timeout.tv_sec = 0;
- timeout.tv_usec = 500000; /* 1/2 second. */
- readable = access(pathname, O_RDONLY);
-
- /* If the file could be opened, then the udev rule has been
- * applied and gavd can read the event files. If there are no
- * event files, then we don't need to wait.
- *
- * If access does not become available, then headphone &
- * microphone jack autoswitching will not function properly.
- */
- if (readable == 0 || (readable == -1 && errno == ENOENT)) {
- /* Access allowed, or file does not exist. */
- break;
- }
- if (readable != -1 || errno != EACCES) {
- syslog(LOG_ERR, "Bad access for input devs.");
- return errno;
- }
- select(1, NULL, NULL, NULL, &timeout);
- ++i;
+ struct cras_alsa_jack *jack;
+ struct cras_alsa_jack_list *jack_list = data->jack_list;
+ struct ucm_section *section = data->section;
+ enum CRAS_STREAM_DIRECTION direction = jack_list->direction;
+ int r;
+
+ r = create_jack_for_gpio(jack_list, pathname, section->jack_name,
+ switch_event, &jack);
+ if (r != 0)
+ return r;
+
+ jack->ucm_device = strdup(section->name);
+ if (!jack->ucm_device) {
+ cras_free_jack(jack, 0);
+ return -ENOMEM;
}
- return 0;
+ if (direction == CRAS_STREAM_OUTPUT)
+ jack->mixer_output = cras_alsa_mixer_get_control_for_section(
+ jack_list->mixer, section);
+ else if (direction == CRAS_STREAM_INPUT)
+ jack->mixer_input = cras_alsa_mixer_get_control_for_section(
+ jack_list->mixer, section);
+
+ return cras_complete_gpio_jack(data, jack, switch_event);
}
-static void find_gpio_jacks(struct cras_alsa_jack_list *jack_list,
- unsigned int card_index,
- const char *card_name,
- enum CRAS_STREAM_DIRECTION direction)
+/* Monitor GPIO switches for this jack_list.
+ * Args:
+ * data - Data for GPIO switch search.
+ * dev_path - Device full path.
+ * dev_name - Device name.
+ * Returns:
+ * 0 for success, or negative on error. Assumes success if no jack is
+ * found, or if the jack could not be accessed.
+ */
+static int gpio_switches_monitor_device(struct gpio_switch_list_data *data,
+ const char *dev_path,
+ const char *dev_name)
{
- /* GPIO switches are on Arm-based machines, and are
- * only associated with on-board devices.
- */
- char *devices[32];
- unsigned n_devices;
- unsigned i;
static const int out_switches[] = {SW_HEADPHONE_INSERT,
SW_LINEOUT_INSERT};
static const int in_switches[] = {SW_MICROPHONE_INSERT};
+ int sw;
+ const int *switches = out_switches;
+ int num_switches = ARRAY_SIZE(out_switches);
+ int success = 1;
+ int rc = 0;
- if (wait_for_dev_input_access())
- return;
+ if (data->section && data->section->jack_switch >= 0) {
+ switches = &data->section->jack_switch;
+ num_switches = 1;
+ }
+ else if (data->jack_list->direction == CRAS_STREAM_INPUT) {
+ switches = in_switches;
+ num_switches = ARRAY_SIZE(in_switches);
+ }
+
+ /* Assume that -EIO is returned for jacks that we shouldn't
+ * be looking at, but stop trying if we run into another
+ * type of error.
+ */
+ for (sw = 0; (rc == 0 || rc == -EIO)
+ && sw < num_switches; sw++) {
+ if (data->section)
+ rc = open_and_monitor_gpio_with_section(
+ data, dev_path, switches[sw]);
+ else
+ rc = open_and_monitor_gpio(
+ data, dev_path, dev_name, switches[sw]);
+ if (rc != 0 && rc != -EIO)
+ success = 0;
+ }
+
+ if (success)
+ return 0;
+ return rc;
+}
+
+static int gpio_switch_list_with_section(const char *dev_path,
+ const char *dev_name,
+ void *arg)
+{
+ struct gpio_switch_list_data *data =
+ (struct gpio_switch_list_data *)arg;
+
+ if (strcmp(dev_name, data->section->jack_name)) {
+ /* No match: continue searching. */
+ return 0;
+ }
+
+ data->rc = gpio_switches_monitor_device(data, dev_path, dev_name);
+ /* Found the only possible match: stop searching. */
+ return 1;
+}
+
+/* Match the given jack name to the given regular expression.
+ * Args:
+ * jack_name - The jack's name.
+ * re - Regular expression string.
+ * Returns:
+ * Non-zero for success, or 0 for failure.
+ */
+static int jack_matches_regex(const char *jack_name, const char *re)
+{
+ regmatch_t m[1];
+ regex_t regex;
+ int rc;
- n_devices = gpio_get_switch_names(direction, devices,
- ARRAY_SIZE(devices));
- for (i = 0; i < n_devices; ++i) {
- int sw;
- const int *switches = out_switches;
- int num_switches = ARRAY_SIZE(out_switches);
+ rc = regcomp(&regex, re, REG_EXTENDED);
+ if (rc != 0) {
+ syslog(LOG_ERR, "Failed to compile regular expression: %s", re);
+ return 0;
+ }
+
+ rc = regexec(&regex, jack_name, ARRAY_SIZE(m), m, 0) == 0;
+ regfree(&regex);
+ return rc;
+}
+
+static int gpio_switch_list_by_matching(const char *dev_path,
+ const char *dev_name,
+ void *arg)
+{
+ struct gpio_switch_list_data *data =
+ (struct gpio_switch_list_data *)arg;
- if (direction == CRAS_STREAM_INPUT) {
- switches = in_switches;
- num_switches = ARRAY_SIZE(in_switches);
+ if (data->jack_list->direction == CRAS_STREAM_INPUT) {
+ if (!jack_matches_regex(dev_name, "^.*Mic Jack$") &&
+ !jack_matches_regex(dev_name, "^.*Headset Jack$")) {
+ /* Continue searching. */
+ return 0;
}
+ }
+ else if (data->jack_list->direction == CRAS_STREAM_OUTPUT) {
+ if (!jack_matches_regex(dev_name, "^.*Headphone Jack$") &&
+ !jack_matches_regex(dev_name, "^.*Headset Jack$") &&
+ !jack_matches_regex(dev_name, "^.*HDMI Jack$")) {
+ /* Continue searching. */
+ return 0;
+ }
+ }
- for (sw = 0; sw < num_switches; sw++)
- open_and_monitor_gpio(jack_list, direction, card_name,
- devices[i], switches[sw]);
- free(devices[i]);
+ data->rc = gpio_switches_monitor_device(data, dev_path, dev_name);
+ /* Stop searching for failure. */
+ return data->rc;
+}
+
+/* Find GPIO jacks for this jack_list.
+ * Args:
+ * jack_list - Jack list to add to.
+ * section - UCM section.
+ * result_jack - Filled with a pointer to the resulting cras_alsa_jack.
+ * Returns:
+ * 0 for success, or negative on error. Assumes success if no jack is
+ * found, or if the jack could not be accessed.
+ */
+static int find_gpio_jacks(struct cras_alsa_jack_list *jack_list,
+ struct ucm_section *section,
+ struct cras_alsa_jack **result_jack)
+{
+ /* GPIO switches are on Arm-based machines, and are
+ * only associated with on-board devices.
+ */
+ struct gpio_switch_list_data data;
+ int rc;
+
+ rc = wait_for_dev_input_access();
+ if (rc != 0) {
+ syslog(LOG_WARNING, "Could not access /dev/input/event0: %s",
+ strerror(rc));
+ return 0;
}
+
+ data.jack_list = jack_list;
+ data.section = section;
+ data.result_jack = NULL;
+ data.rc = 0;
+
+ if (section)
+ 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)
+ *result_jack = data.result_jack;
+ return data.rc;
}
/* Callback from alsa when a jack control changes. This is registered with
@@ -599,23 +820,6 @@ static int hctl_jack_cb(snd_hctl_elem_t *elem, unsigned int mask)
return 0;
}
-/* Handles notifications from alsa controls. Called by main thread when a poll
- * fd provided by alsa signals there is an event available. */
-static void alsa_control_event_pending(void *arg)
-{
- struct cras_alsa_jack_list *jack_list;
-
- jack_list = (struct cras_alsa_jack_list *)arg;
- if (jack_list == NULL) {
- syslog(LOG_ERR, "Invalid jack_list from control event.");
- return;
- }
-
- /* handle_events will trigger the callback registered with each control
- * that has changed. */
- snd_hctl_handle_events(jack_list->hctl);
-}
-
/* Determines the device associated with this jack if any. If the device cannot
* be determined (common case), assume device 0. */
static unsigned int hctl_jack_device_index(const char *name)
@@ -659,63 +863,10 @@ static int is_jack_control_in_list(const char * const *list,
return 0;
}
-/* Registers each poll fd (one per jack) with the system so that they are passed
- * to select in the main loop. */
-static int add_jack_poll_fds(struct cras_alsa_jack_list *jack_list)
-{
- struct pollfd *pollfds;
- nfds_t n;
- unsigned int i;
- int rc = 0;
-
- n = snd_hctl_poll_descriptors_count(jack_list->hctl);
- if (n == 0)
- return 0;
-
- pollfds = malloc(n * sizeof(*pollfds));
- if (pollfds == NULL)
- return -ENOMEM;
-
- n = snd_hctl_poll_descriptors(jack_list->hctl, pollfds, n);
- for (i = 0; i < n; i++) {
- struct jack_poll_fd *registered_fd;
-
- registered_fd = calloc(1, sizeof(*registered_fd));
- if (registered_fd == NULL) {
- rc = -ENOMEM;
- break;
- }
- registered_fd->fd = pollfds[i].fd;
- DL_APPEND(jack_list->registered_fds, registered_fd);
- rc = cras_system_add_select_fd(registered_fd->fd,
- alsa_control_event_pending,
- jack_list);
- if (rc < 0)
- break;
- }
- free(pollfds);
- return rc;
-}
-
-/* Cancels registration of each poll fd (one per jack) with the system. */
-static void remove_jack_poll_fds(struct cras_alsa_jack_list *jack_list)
-{
- struct jack_poll_fd *registered_fd;
-
- DL_FOREACH(jack_list->registered_fds, registered_fd) {
- cras_system_rm_select_fd(registered_fd->fd);
- DL_DELETE(jack_list->registered_fds, registered_fd);
- free(registered_fd);
- }
-}
-
/* Looks for any JACK controls. Monitors any found controls for changes and
* decides to route based on plug/unlpug events. */
-static int find_jack_controls(struct cras_alsa_jack_list *jack_list,
- const char *device_name,
- enum CRAS_STREAM_DIRECTION direction)
+static int find_jack_controls(struct cras_alsa_jack_list *jack_list)
{
- int rc;
snd_hctl_elem_t *elem;
struct cras_alsa_jack *jack;
const char *name;
@@ -731,8 +882,14 @@ static int find_jack_controls(struct cras_alsa_jack_list *jack_list,
static const char eld_control_name[] = "ELD";
const char * const *jack_names;
unsigned int num_jack_names;
+ char device_name[6];
+
+ if (!jack_list->hctl) {
+ syslog(LOG_WARNING, "Can't search hctl for jacks.");
+ return 0;
+ }
- if (direction == CRAS_STREAM_OUTPUT) {
+ if (jack_list->direction == CRAS_STREAM_OUTPUT) {
jack_names = output_jack_base_names;
num_jack_names = ARRAY_SIZE(output_jack_base_names);
} else {
@@ -740,21 +897,6 @@ static int find_jack_controls(struct cras_alsa_jack_list *jack_list,
num_jack_names = ARRAY_SIZE(input_jack_base_names);
}
- rc = snd_hctl_open(&jack_list->hctl, device_name, SND_CTL_NONBLOCK);
- if (rc < 0) {
- syslog(LOG_ERR, "failed to get hctl for %s", device_name);
- return rc;
- }
- rc = snd_hctl_nonblock(jack_list->hctl, 1);
- if (rc < 0) {
- syslog(LOG_ERR, "failed to nonblock hctl for %s", device_name);
- return rc;
- }
- rc = snd_hctl_load(jack_list->hctl);
- if (rc < 0) {
- syslog(LOG_ERR, "failed to load hctl for %s", device_name);
- return rc;
- }
for (elem = snd_hctl_first_elem(jack_list->hctl); elem != NULL;
elem = snd_hctl_elem_next(elem)) {
snd_ctl_elem_iface_t iface;
@@ -779,7 +921,7 @@ static int find_jack_controls(struct cras_alsa_jack_list *jack_list,
snd_hctl_elem_set_callback(elem, hctl_jack_cb);
snd_hctl_elem_set_callback_private(elem, jack);
- if (direction == CRAS_STREAM_OUTPUT)
+ if (jack_list->direction == CRAS_STREAM_OUTPUT)
jack->mixer_output =
cras_alsa_mixer_get_output_matching_name(
jack_list->mixer,
@@ -787,9 +929,9 @@ static int find_jack_controls(struct cras_alsa_jack_list *jack_list,
if (jack_list->ucm)
jack->ucm_device =
ucm_get_dev_for_jack(jack_list->ucm, name,
- direction);
+ jack_list->direction);
- if (jack->ucm_device && direction == CRAS_STREAM_INPUT) {
+ if (jack->ucm_device && jack_list->direction == CRAS_STREAM_INPUT) {
char *control_name;
control_name = ucm_get_cap_control(jack->jack_list->ucm,
jack->ucm_device);
@@ -803,7 +945,7 @@ static int find_jack_controls(struct cras_alsa_jack_list *jack_list,
if (jack->ucm_device) {
jack->dsp_name = ucm_get_dsp_name(
jack->jack_list->ucm, jack->ucm_device,
- direction);
+ jack_list->direction);
jack->override_type_name = ucm_get_override_type_name(
jack->jack_list->ucm, jack->ucm_device);
}
@@ -811,6 +953,8 @@ static int find_jack_controls(struct cras_alsa_jack_list *jack_list,
/* Look up ELD controls */
DL_FOREACH(jack_list->jacks, jack) {
+ if (jack->is_gpio || jack->eld_control)
+ continue;
name = snd_hctl_elem_get_name(jack->elem);
if (!is_jack_hdmi_dp(name))
continue;
@@ -827,14 +971,89 @@ static int find_jack_controls(struct cras_alsa_jack_list *jack_list,
}
}
- /* If we have found jacks, have the poll fds passed to select in the
- * main loop. */
- if (jack_list->jacks != NULL) {
- rc = add_jack_poll_fds(jack_list);
- if (rc < 0)
- return rc;
+ return 0;
+}
+
+/*
+ * Exported Interface.
+ */
+
+int cras_alsa_jack_list_find_jacks_by_name_matching(
+ struct cras_alsa_jack_list *jack_list)
+{
+ int rc;
+
+ rc = find_jack_controls(jack_list);
+ if (rc != 0)
+ return rc;
+
+ return find_gpio_jacks(jack_list, NULL, NULL);
+}
+
+static int find_hctl_jack_for_section(
+ struct cras_alsa_jack_list *jack_list,
+ 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;
+
+ if (!jack_list->hctl) {
+ syslog(LOG_WARNING, "Can't search hctl for jacks.");
+ return -ENODEV;
+ }
+
+ 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_CARD);
+ snd_ctl_elem_id_set_device(elem_id, jack_list->device_index);
+ snd_ctl_elem_id_set_name(elem_id, section->jack_name);
+ elem = snd_hctl_find_elem(jack_list->hctl, elem_id);
+ if (!elem)
+ return -ENOENT;
+
+ syslog(LOG_DEBUG, "Found Jack: %s for %s",
+ section->jack_name, section->name);
+
+ jack = cras_alloc_jack(0);
+ if (jack == NULL)
+ return -ENOMEM;
+ jack->elem = elem;
+ jack->jack_list = jack_list;
+
+ jack->ucm_device = strdup(section->name);
+ if (!jack->ucm_device) {
+ free(jack);
+ return -ENOMEM;
}
+ if (jack_list->direction == CRAS_STREAM_OUTPUT)
+ jack->mixer_output = cras_alsa_mixer_get_control_for_section(
+ jack_list->mixer, section);
+ else if (jack_list->direction == CRAS_STREAM_INPUT)
+ jack->mixer_input = cras_alsa_mixer_get_control_for_section(
+ jack_list->mixer, section);
+
+ jack->dsp_name = ucm_get_dsp_name(
+ jack->jack_list->ucm, jack->ucm_device,
+ jack_list->direction);
+
+ snd_hctl_elem_set_callback(elem, hctl_jack_cb);
+ snd_hctl_elem_set_callback_private(elem, jack);
+ DL_APPEND(jack_list->jacks, jack);
+ if (result_jack)
+ *result_jack = jack;
+
+ if (!strcmp(jack->ucm_device, "HDMI") ||
+ !strcmp(jack->ucm_device, "DP"))
+ 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;
return 0;
}
@@ -842,30 +1061,58 @@ static int find_jack_controls(struct cras_alsa_jack_list *jack_list,
* Exported Interface.
*/
+int cras_alsa_jack_list_add_jack_for_section(
+ struct cras_alsa_jack_list *jack_list,
+ struct ucm_section *ucm_section,
+ struct cras_alsa_jack **result_jack)
+{
+ if (result_jack)
+ *result_jack = NULL;
+ if (!ucm_section)
+ return -EINVAL;
+
+ if (!ucm_section->jack_name) {
+ /* No jacks defined for this device. */
+ return 0;
+ }
+
+ if (!ucm_section->jack_type) {
+ syslog(LOG_ERR,
+ "Must specify the JackType for jack '%s' in '%s'.",
+ ucm_section->jack_name, ucm_section->name);
+ return -EINVAL;
+ }
+
+ if (!strcmp(ucm_section->jack_type, "hctl")) {
+ return find_hctl_jack_for_section(
+ jack_list, ucm_section, result_jack);
+ } else if (!strcmp(ucm_section->jack_type, "gpio")) {
+ return find_gpio_jacks(jack_list, ucm_section, result_jack);
+ } else {
+ syslog(LOG_ERR,
+ "Invalid JackType '%s' in '%s'.",
+ ucm_section->jack_type, ucm_section->name);
+ return -EINVAL;
+ }
+}
+
struct cras_alsa_jack_list *cras_alsa_jack_list_create(
unsigned int card_index,
const char *card_name,
unsigned int device_index,
int is_first_device,
struct cras_alsa_mixer *mixer,
- snd_use_case_mgr_t *ucm,
+ struct cras_use_case_mgr *ucm,
+ snd_hctl_t *hctl,
enum CRAS_STREAM_DIRECTION direction,
jack_state_change_callback *cb,
void *cb_data)
{
struct cras_alsa_jack_list *jack_list;
- char device_name[6];
if (direction != CRAS_STREAM_INPUT && direction != CRAS_STREAM_OUTPUT)
return NULL;
- /* Enforce alsa limits. */
- if (card_index >= 32 || device_index >= 32) {
- syslog(LOG_ERR, "Jack List: Invalid card/dev %u/%u",
- card_index, device_index);
- return NULL;
- }
-
jack_list = (struct cras_alsa_jack_list *)calloc(1, sizeof(*jack_list));
if (jack_list == NULL)
return NULL;
@@ -874,17 +1121,12 @@ struct cras_alsa_jack_list *cras_alsa_jack_list_create(
jack_list->callback_data = cb_data;
jack_list->mixer = mixer;
jack_list->ucm = ucm;
+ jack_list->hctl = hctl;
+ jack_list->card_index = card_index;
+ jack_list->card_name = card_name;
jack_list->device_index = device_index;
jack_list->is_first_device = is_first_device;
-
- snprintf(device_name, sizeof(device_name), "hw:%d", card_index);
-
- if (find_jack_controls(jack_list, device_name, direction) != 0) {
- cras_alsa_jack_list_destroy(jack_list);
- return NULL;
- }
-
- find_gpio_jacks(jack_list, card_index, card_name, direction);
+ jack_list->direction = direction;
return jack_list;
}
@@ -895,30 +1137,24 @@ void cras_alsa_jack_list_destroy(struct cras_alsa_jack_list *jack_list)
if (jack_list == NULL)
return;
- remove_jack_poll_fds(jack_list);
DL_FOREACH(jack_list->jacks, jack) {
DL_DELETE(jack_list->jacks, jack);
- free(jack->ucm_device);
-
- free((void *)jack->edid_file);
- if (jack->display_info_timer)
- cras_tm_cancel_timer(cras_system_state_get_tm(),
- jack->display_info_timer);
-
- if (jack->is_gpio) {
- free(jack->gpio.device_name);
- close(jack->gpio.fd);
- }
+ cras_free_jack(jack, 1);
+ }
+ free(jack_list);
+}
- if (jack->override_type_name)
- free((void *)jack->override_type_name);
+int cras_alsa_jack_list_has_hctl_jacks(struct cras_alsa_jack_list *jack_list)
+{
+ struct cras_alsa_jack *jack;
- free((void *)jack->dsp_name);
- free(jack);
+ if (!jack_list)
+ return 0;
+ DL_FOREACH(jack_list->jacks, jack) {
+ if (!jack->is_gpio)
+ return 1;
}
- if (jack_list->hctl)
- snd_hctl_close(jack_list->hctl);
- free(jack_list);
+ return 0;
}
struct mixer_control *cras_alsa_jack_get_mixer_output(
@@ -958,6 +1194,11 @@ const char *cras_alsa_jack_get_name(const struct cras_alsa_jack *jack)
return snd_hctl_elem_get_name(jack->elem);
}
+const char *cras_alsa_jack_get_ucm_device(const struct cras_alsa_jack *jack)
+{
+ return jack->ucm_device;
+}
+
void cras_alsa_jack_update_monitor_name(const struct cras_alsa_jack *jack,
char *name_buf,
unsigned int buf_size)
diff --git a/cras/src/server/cras_alsa_jack.h b/cras/src/server/cras_alsa_jack.h
index 9b25adf9..b944898e 100644
--- a/cras/src/server/cras_alsa_jack.h
+++ b/cras/src/server/cras_alsa_jack.h
@@ -27,8 +27,8 @@ typedef void (jack_state_change_callback)(const struct cras_alsa_jack *jack,
int plugged,
void *data);
-/* Creates a jack list. The list holds all the interesting ALSA jacks for this
- * device. These jacks will be for headphones, speakers, HDMI, etc.
+/* Creates a jack list. The jacks can be added later by name matching or
+ * fully specified UCM.
* Args:
* card_index - Index ALSA uses to refer to the card. The X in "hw:X".
* card_name - The name of the card (used to find gpio jacks).
@@ -37,7 +37,8 @@ typedef void (jack_state_change_callback)(const struct cras_alsa_jack *jack,
* mixer - The mixer associated with this card, used to find controls that
* correspond to jacks. For instance "Headphone switch" for "Headphone
* Jack".
- * ucm - ALSA use case manager if available.
+ * ucm - CRAS use case manager if available.
+ * hctl - ALSA high-level control interface if available.
* direction - Input or output, look for mic or headphone jacks.
* cb - Function to call when a jack state changes.
* cb_data - Passed to the callback when called.
@@ -50,17 +51,48 @@ struct cras_alsa_jack_list *cras_alsa_jack_list_create(
unsigned int device_index,
int is_first_device,
struct cras_alsa_mixer *mixer,
- snd_use_case_mgr_t *ucm,
+ struct cras_use_case_mgr *ucm,
+ snd_hctl_t *hctl,
enum CRAS_STREAM_DIRECTION direction,
jack_state_change_callback *cb,
void *cb_data);
+/* Finds jacks by name matching.
+ * The list holds all the interesting ALSA jacks for this
+ * device. These jacks will be for headphones, speakers, HDMI, etc.
+ * Args:
+ * jack_list - A pointer to a jack list.
+ * Returns:
+ * 0 on success. Error code if there is a failure.
+ */
+int cras_alsa_jack_list_find_jacks_by_name_matching(
+ struct cras_alsa_jack_list *jack_list);
+
+/* Add the jack defined by the UCM section information.
+ * Args:
+ * jack_list - A pointer to a jack list.
+ * ucm_section - UCM section data.
+ * result_jack - Resulting jack that was added.
+ * Returns:
+ * 0 on success. Error code if there is a failure.
+ */
+int cras_alsa_jack_list_add_jack_for_section(
+ struct cras_alsa_jack_list *jack_list,
+ struct ucm_section *ucm_section,
+ struct cras_alsa_jack **result_jack);
+
/* Destroys a jack list created with cras_alsa_jack_list_create.
* Args:
* jack_list - The list to destroy.
*/
void cras_alsa_jack_list_destroy(struct cras_alsa_jack_list *jack_list);
+/* Returns non-zero if the jack list has hctl jacks.
+ * Args:
+ * jack_list - The list check.
+ */
+int cras_alsa_jack_list_has_hctl_jacks(struct cras_alsa_jack_list *jack_list);
+
/* Gets the mixer output associated with the given jack.
* Args:
* jack - The jack to query for a mixer output.
@@ -91,6 +123,12 @@ void cras_alsa_jack_list_report(const struct cras_alsa_jack_list *jack_list);
*/
const char *cras_alsa_jack_get_name(const struct cras_alsa_jack *jack);
+/* Gets the ucm device of a jack.
+ * Args:
+ * jack - The alsa jack.
+ */
+const char *cras_alsa_jack_get_ucm_device(const struct cras_alsa_jack *jack);
+
void cras_alsa_jack_update_monitor_name(const struct cras_alsa_jack *jack,
char *name_buf,
diff --git a/cras/src/server/cras_alsa_mixer.c b/cras/src/server/cras_alsa_mixer.c
index fe3e2d09..4cb665a3 100644
--- a/cras/src/server/cras_alsa_mixer.c
+++ b/cras/src/server/cras_alsa_mixer.c
@@ -4,40 +4,67 @@
*/
#include <alsa/asoundlib.h>
+#include <limits.h>
#include <stdio.h>
#include <syslog.h>
#include "cras_alsa_mixer.h"
-#include "cras_card_config.h"
+#include "cras_alsa_mixer_name.h"
+#include "cras_alsa_ucm.h"
#include "cras_util.h"
-#include "cras_volume_curve.h"
#include "utlist.h"
+#define MIXER_CONTROL_VOLUME_DB_INVALID LONG_MAX
+
/* Represents an ALSA control element. Each device can have several of these,
* each potentially having independent volume and mute controls.
* elem - ALSA mixer element.
* has_volume - non-zero indicates there is a volume control.
* has_mute - non-zero indicates there is a mute switch.
+ * max_volume_dB - the maximum volume for this control, or
+ * MIXER_CONTROL_VOLUME_DB_INVALID.
+ * min_volume_dB - the minimum volume for this control, or
+ * MIXER_CONTROL_VOLUME_DB_INVALID.
*/
-struct mixer_control {
+struct mixer_control_element {
snd_mixer_elem_t *elem;
int has_volume;
int has_mute;
- struct mixer_control *prev, *next;
+ long max_volume_dB;
+ long min_volume_dB;
+ struct mixer_control_element *prev, *next;
};
-/* Represents an ALSA control element related to a specific output such as
- * speakers or headphones. A device can have several of these, each potentially
- * having independent volume and mute controls.
+/* Represents an ALSA control element related to a specific input/output
+ * node such as speakers or headphones. A device can have several of these,
+ * each potentially having independent volume and mute controls.
+ *
+ * Each will have at least one mixer_control_element. For cases where there
+ * are separate control elements for left/right channels (for example),
+ * additional mixer_control_elements are added.
+ *
+ * For controls with volume it is assumed that all elements have the same
+ * range.
+ *
+ * name - Name of the control (typicially this is the same as the name of the
+ * mixer_control_element when there is one, or the name of the UCM
+ * parent when there are multiple).
+ * dir - Control direction, OUTPUT or INPUT only.
+ * elements - The mixer_control_elements that are driven by this control.
+ * has_volume - non-zero indicates there is a volume control.
+ * has_mute - non-zero indicates there is a mute switch.
* max_volume_dB - Maximum volume available in the volume control.
* min_volume_dB - Minimum volume available in the volume control.
- * volume_curve - Curve for this output.
*/
-struct mixer_output_control {
- struct mixer_control base;
+struct mixer_control {
+ const char *name;
+ enum CRAS_STREAM_DIRECTION dir;
+ struct mixer_control_element *elements;
+ int has_volume;
+ int has_mute;
long max_volume_dB;
long min_volume_dB;
- struct cras_volume_curve *volume_curve;
+ struct mixer_control *prev, *next;
};
/* Holds a reference to the opened mixer and the volume controls.
@@ -46,11 +73,9 @@ struct mixer_output_control {
* playback_switch - Switch used to mute the device.
* main_capture_controls - List of capture gain controls (normally 'Capture').
* capture_switch - Switch used to mute the capture stream.
- * volume_curve - Default volume curve that converts from an index to dBFS.
* max_volume_dB - Maximum volume available in main volume controls. The dBFS
* value setting will be applied relative to this.
* min_volume_dB - Minimum volume available in main volume controls.
- * config - Config info for this card, can be NULL if none found.
*/
struct cras_alsa_mixer {
snd_mixer_t *mixer;
@@ -60,95 +85,404 @@ struct cras_alsa_mixer {
struct mixer_control *main_capture_controls;
struct mixer_control *input_controls;
snd_mixer_elem_t *capture_switch;
- struct cras_volume_curve *volume_curve;
long max_volume_dB;
long min_volume_dB;
- const struct cras_card_config *config;
};
/* Wrapper for snd_mixer_open and helpers.
* Args:
* mixdev - Name of the device to open the mixer for.
- * Returns:
- * pointer to opened mixer on success, NULL on failure.
+ * mixer - Pointer filled with the opened mixer on success, NULL on failure.
*/
-static snd_mixer_t *alsa_mixer_open(const char *mixdev)
+static void alsa_mixer_open(const char *mixdev,
+ snd_mixer_t **mixer)
{
- snd_mixer_t *mixer = NULL;
int rc;
- rc = snd_mixer_open(&mixer, 0);
- if (rc < 0)
- return NULL;
- rc = snd_mixer_attach(mixer, mixdev);
- if (rc < 0)
+
+ *mixer = NULL;
+ rc = snd_mixer_open(mixer, 0);
+ if (rc < 0) {
+ syslog(LOG_ERR, "snd_mixer_open: %d: %s", rc, strerror(-rc));
+ return;
+ }
+ rc = snd_mixer_attach(*mixer, mixdev);
+ if (rc < 0) {
+ syslog(LOG_ERR, "snd_mixer_attach: %d: %s", rc, strerror(-rc));
goto fail_after_open;
- rc = snd_mixer_selem_register(mixer, NULL, NULL);
- if (rc < 0)
+ }
+ rc = snd_mixer_selem_register(*mixer, NULL, NULL);
+ if (rc < 0) {
+ syslog(LOG_ERR, "snd_mixer_selem_register: %d: %s", rc, strerror(-rc));
goto fail_after_open;
- rc = snd_mixer_load(mixer);
- if (rc < 0)
+ }
+ rc = snd_mixer_load(*mixer);
+ if (rc < 0) {
+ syslog(LOG_ERR, "snd_mixer_load: %d: %s", rc, strerror(-rc));
goto fail_after_open;
- return mixer;
+ }
+ return;
fail_after_open:
- snd_mixer_close(mixer);
- return NULL;
+ snd_mixer_close(*mixer);
+ *mixer = NULL;
+}
+
+static struct mixer_control_element *mixer_control_element_create(
+ snd_mixer_elem_t *elem,
+ enum CRAS_STREAM_DIRECTION dir)
+{
+ struct mixer_control_element *c;
+ long min, max;
+
+ if (!elem)
+ return NULL;
+
+ c = (struct mixer_control_element *)calloc(1, sizeof(*c));
+ if (!c) {
+ syslog(LOG_ERR, "No memory for mixer_control_elem.");
+ return NULL;
+ }
+
+ c->elem = elem;
+ c->max_volume_dB = MIXER_CONTROL_VOLUME_DB_INVALID;
+ c->min_volume_dB = MIXER_CONTROL_VOLUME_DB_INVALID;
+
+ if (dir == CRAS_STREAM_OUTPUT) {
+ c->has_mute = snd_mixer_selem_has_playback_switch(elem);
+
+ if (snd_mixer_selem_has_playback_volume(elem) &&
+ snd_mixer_selem_get_playback_dB_range(
+ elem, &min, &max) == 0) {
+ c->max_volume_dB = max;
+ c->min_volume_dB = min;
+ c->has_volume = 1;
+ }
+ }
+ else if (dir == CRAS_STREAM_INPUT) {
+ c->has_mute = snd_mixer_selem_has_capture_switch(elem);
+
+ if (snd_mixer_selem_has_capture_volume(elem) &&
+ snd_mixer_selem_get_capture_dB_range(
+ elem, &min, &max) == 0) {
+ c->max_volume_dB = max;
+ c->min_volume_dB = min;
+ c->has_volume = 1;
+ }
+ }
+
+ return c;
+}
+
+static void mixer_control_destroy(struct mixer_control *control) {
+ struct mixer_control_element *elem;
+
+ if (!control)
+ return;
+
+ DL_FOREACH(control->elements, elem) {
+ DL_DELETE(control->elements, elem);
+ free(elem);
+ }
+ if (control->name)
+ free((void *)control->name);
+ free(control);
+}
+
+static void mixer_control_destroy_list(struct mixer_control *control_list)
+{
+ struct mixer_control *control;
+ if (!control_list)
+ return;
+ DL_FOREACH(control_list, control) {
+ DL_DELETE(control_list, control);
+ mixer_control_destroy(control);
+ }
+}
+
+static int mixer_control_add_element(struct mixer_control *control,
+ snd_mixer_elem_t *snd_elem)
+{
+ struct mixer_control_element *elem;
+
+ if (!control)
+ return -EINVAL;
+
+ elem = mixer_control_element_create(snd_elem, control->dir);
+ if (!elem)
+ return -ENOMEM;
+
+ DL_APPEND(control->elements, elem);
+
+ if (elem->has_volume) {
+ if (!control->has_volume)
+ control->has_volume = 1;
+
+ /* Assume that all elements have a common volume range, and
+ * that both min and max values are valid if one of the two
+ * is valid. */
+ if (control->min_volume_dB ==
+ MIXER_CONTROL_VOLUME_DB_INVALID) {
+ control->min_volume_dB = elem->min_volume_dB;
+ control->max_volume_dB = elem->max_volume_dB;
+ } else if (control->min_volume_dB != elem->min_volume_dB ||
+ control->max_volume_dB != elem->max_volume_dB) {
+ syslog(LOG_WARNING,
+ "Element '%s' of control '%s' has different"
+ "volume range: [%ld:%ld] ctrl: [%ld:%ld]",
+ snd_mixer_selem_get_name(elem->elem),
+ control->name,
+ elem->min_volume_dB, elem->max_volume_dB,
+ control->min_volume_dB, control->max_volume_dB);
+ }
+ }
+
+ if (elem->has_mute && !control->has_mute)
+ control->has_mute = 1;
+ return 0;
+}
+
+static int mixer_control_create(struct mixer_control **control,
+ const char *name,
+ snd_mixer_elem_t *elem,
+ enum CRAS_STREAM_DIRECTION dir)
+{
+ struct mixer_control *c;
+ int rc = 0;
+
+ if (!control)
+ return -EINVAL;
+
+ c = (struct mixer_control *)calloc(1, sizeof(*c));
+ if (!c) {
+ syslog(LOG_ERR, "No memory for mixer_control: %s", name);
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ c->dir = dir;
+ c->min_volume_dB = MIXER_CONTROL_VOLUME_DB_INVALID;
+ c->max_volume_dB = MIXER_CONTROL_VOLUME_DB_INVALID;
+
+ if (!name && elem)
+ name = snd_mixer_selem_get_name(elem);
+ if (!name) {
+ syslog(LOG_ERR, "Control does not have a name.");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ c->name = strdup(name);
+ if (!c->name) {
+ syslog(LOG_ERR, "No memory for control's name: %s", name);
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ if (elem && (rc = mixer_control_add_element(c, elem)))
+ goto error;
+
+ *control = c;
+ return 0;
+
+error:
+ mixer_control_destroy(c);
+ *control = NULL;
+ return rc;
}
-/* Checks if the given element's name is in the list. */
-static int name_in_list(const char *name,
- const char * const list[],
- size_t len)
+/* Creates a mixer_control by finding mixer element names in simple mixer
+ * interface.
+ * Args:
+ * control[out] - Storage for resulting pointer to mixer_control.
+ * cmix[in] - Parent alsa mixer.
+ * name[in] - Optional name of the control. Input NULL to take the name of
+ * the first element from mixer_names.
+ * mixer_names[in] - Names of the ASLA mixer control elements. Must not
+ * be empty.
+ * dir[in] - Control direction: CRAS_STREAM_OUTPUT or CRAS_STREAM_INPUT.
+ * Returns:
+ * Returns 0 for success, negative error code otherwise. *control is
+ * initialized to NULL on error, or has a valid pointer for success.
+ */
+static int mixer_control_create_by_name(
+ struct mixer_control **control,
+ struct cras_alsa_mixer *cmix,
+ const char *name,
+ struct mixer_name *mixer_names,
+ enum CRAS_STREAM_DIRECTION dir)
{
- size_t i;
+ snd_mixer_selem_id_t *sid;
+ snd_mixer_elem_t *elem;
+ struct mixer_control *c;
+ struct mixer_name *m_name;
+ int rc;
+
+ if (!control)
+ return -EINVAL;
+ *control = NULL;
+ if (!mixer_names)
+ return -EINVAL;
+ if (!name) {
+ /* Assume that we're using the first name in the list of mixer
+ * names. */
+ name = mixer_names->name;
+ }
+
+ rc = mixer_control_create(&c, name, NULL, dir);
+ if (rc)
+ return rc;
+
+ snd_mixer_selem_id_malloc(&sid);
+
+ DL_FOREACH(mixer_names, m_name) {
+ snd_mixer_selem_id_set_index(sid, 0);
+ snd_mixer_selem_id_set_name(sid, m_name->name);
+ elem = snd_mixer_find_selem(cmix->mixer, sid);
+ if (!elem) {
+ mixer_control_destroy(c);
+ snd_mixer_selem_id_free(sid);
+ syslog(LOG_ERR, "Unable to find simple control %s, 0",
+ m_name->name);
+ return -ENOENT;
+ }
+ rc = mixer_control_add_element(c, elem);
+ if (rc) {
+ mixer_control_destroy(c);
+ snd_mixer_selem_id_free(sid);
+ return rc;
+ }
+ }
- for (i = 0; i < len; i++)
- if (list[i] && strcmp(list[i], name) == 0)
- return 1;
+ snd_mixer_selem_id_free(sid);
+ *control = c;
return 0;
}
+static int mixer_control_set_dBFS(
+ const struct mixer_control *control, long to_set)
+{
+ const struct mixer_control_element *elem = NULL;
+ int rc = -EINVAL;
+ if (!control)
+ return rc;
+ DL_FOREACH(control->elements, elem) {
+ if(elem->has_volume) {
+ if (control->dir == CRAS_STREAM_OUTPUT)
+ rc = snd_mixer_selem_set_playback_dB_all(
+ elem->elem, to_set, 1);
+ else if (control->dir == CRAS_STREAM_INPUT)
+ rc = snd_mixer_selem_set_capture_dB_all(
+ elem->elem, to_set, 1);
+ if (rc)
+ break;
+ syslog(LOG_DEBUG, "%s:%s volume set to %ld",
+ control->name,
+ snd_mixer_selem_get_name(elem->elem),
+ to_set);
+ }
+ }
+ if (rc && elem) {
+ syslog(LOG_ERR, "Failed to set volume of '%s:%s': %d",
+ control->name,
+ snd_mixer_selem_get_name(elem->elem), rc);
+ }
+ return rc;
+}
+
+static int mixer_control_get_dBFS(
+ const struct mixer_control *control, long *to_get)
+{
+ const struct mixer_control_element *elem = NULL;
+ int rc = -EINVAL;
+ if (!control || !to_get)
+ return -EINVAL;
+ DL_FOREACH(control->elements, elem) {
+ if (elem->has_volume) {
+ if (control->dir == CRAS_STREAM_OUTPUT)
+ rc = snd_mixer_selem_get_playback_dB(
+ elem->elem,
+ SND_MIXER_SCHN_FRONT_LEFT,
+ to_get);
+ else if (control->dir == CRAS_STREAM_INPUT)
+ rc = snd_mixer_selem_get_capture_dB(
+ elem->elem,
+ SND_MIXER_SCHN_FRONT_LEFT,
+ to_get);
+ /* Assume all of the elements of this control have
+ * the same value. */
+ break;
+ }
+ }
+ if (rc && elem) {
+ syslog(LOG_ERR, "Failed to get volume of '%s:%s': %d",
+ control->name,
+ snd_mixer_selem_get_name(elem->elem), rc);
+ }
+ return rc;
+}
+
+static int mixer_control_set_mute(
+ const struct mixer_control *control, int muted)
+{
+ const struct mixer_control_element *elem = NULL;
+ int rc;
+ if (!control)
+ return -EINVAL;
+ DL_FOREACH(control->elements, elem) {
+ if(elem->has_mute) {
+ if (control->dir == CRAS_STREAM_OUTPUT)
+ rc = snd_mixer_selem_set_playback_switch_all(
+ elem->elem, !muted);
+ else if (control->dir == CRAS_STREAM_INPUT)
+ rc = snd_mixer_selem_set_capture_switch_all(
+ elem->elem, !muted);
+ if (rc)
+ break;
+ }
+ }
+ if (rc && elem) {
+ syslog(LOG_ERR, "Failed to mute '%s:%s': %d",
+ control->name,
+ snd_mixer_selem_get_name(elem->elem), rc);
+ }
+ return rc;
+}
+
/* Adds the main volume control to the list and grabs the first seen playback
* switch to use for mute. */
static int add_main_volume_control(struct cras_alsa_mixer *cmix,
snd_mixer_elem_t *elem)
{
if (snd_mixer_selem_has_playback_volume(elem)) {
+ long range;
struct mixer_control *c, *next;
- long min, max;
+ int rc = mixer_control_create(&c, NULL, elem, CRAS_STREAM_OUTPUT);
+ if (rc)
+ return rc;
- c = (struct mixer_control *)calloc(1, sizeof(*c));
- if (c == NULL) {
- syslog(LOG_ERR, "No memory for mixer.");
- return -ENOMEM;
- }
-
- c->elem = elem;
-
- if (snd_mixer_selem_get_playback_dB_range(elem,
- &min,
- &max) == 0) {
- cmix->max_volume_dB += max;
- cmix->min_volume_dB += min;
+ if (c->has_volume) {
+ cmix->max_volume_dB += c->max_volume_dB;
+ cmix->min_volume_dB += c->min_volume_dB;
}
+ range = c->max_volume_dB - c->min_volume_dB;
DL_FOREACH(cmix->main_volume_controls, next) {
- long next_min, next_max;
- snd_mixer_selem_get_playback_dB_range(next->elem,
- &next_min,
- &next_max);
- if (max - min > next_max - next_min)
+ if (range > next->max_volume_dB - next->min_volume_dB)
break;
}
+ syslog(LOG_DEBUG, "Add main volume control %s\n", c->name);
DL_INSERT(cmix->main_volume_controls, next, c);
}
/* If cmix doesn't yet have a playback switch and this is a playback
* switch, use it. */
if (cmix->playback_switch == NULL &&
- snd_mixer_selem_has_playback_switch(elem))
+ snd_mixer_selem_has_playback_switch(elem)) {
+ syslog(LOG_DEBUG, "Using '%s' as playback switch.",
+ snd_mixer_selem_get_name(elem));
cmix->playback_switch = elem;
+ }
return 0;
}
@@ -164,99 +498,62 @@ static int add_main_capture_control(struct cras_alsa_mixer *cmix,
if (snd_mixer_selem_has_capture_volume(elem)) {
struct mixer_control *c;
+ int rc = mixer_control_create(&c, NULL, elem, CRAS_STREAM_INPUT);
+ if (rc)
+ return rc;
- c = (struct mixer_control *)calloc(1, sizeof(*c));
- if (c == NULL) {
- syslog(LOG_ERR, "No memory for control.");
- return -ENOMEM;
- }
-
- c->elem = elem;
-
- syslog(LOG_DEBUG,
- "Add capture control %s\n",
- snd_mixer_selem_get_name(elem));
+ syslog(LOG_DEBUG, "Add main capture control %s\n", c->name);
DL_APPEND(cmix->main_capture_controls, c);
}
/* If cmix doesn't yet have a capture switch and this is a capture
* switch, use it. */
if (cmix->capture_switch == NULL &&
- snd_mixer_selem_has_capture_switch(elem))
+ snd_mixer_selem_has_capture_switch(elem)) {
+ syslog(LOG_DEBUG, "Using '%s' as capture switch.",
+ snd_mixer_selem_get_name(elem));
cmix->capture_switch = elem;
+ }
return 0;
}
-/* Creates a volume curve for a new output. */
-static struct cras_volume_curve *create_volume_curve_for_output(
- const struct cras_alsa_mixer *cmix,
- snd_mixer_elem_t *elem)
-{
- const char *output_name;
-
- output_name = snd_mixer_selem_get_name(elem);
- return cras_card_config_get_volume_curve_for_control(cmix->config,
- output_name);
-}
-
-/* Adds an output control to the list. */
-static int add_output_control(struct cras_alsa_mixer *cmix,
- snd_mixer_elem_t *elem)
+/* Adds a control to the list. */
+static int add_control_with_name(struct cras_alsa_mixer *cmix,
+ enum CRAS_STREAM_DIRECTION dir,
+ snd_mixer_elem_t *elem,
+ const char *name)
{
int index; /* Index part of mixer simple element */
struct mixer_control *c;
- struct mixer_output_control *output;
- long min, max;
+ int rc;
index = snd_mixer_selem_get_index(elem);
- syslog(LOG_DEBUG, "Add output control: %s,%d\n",
- snd_mixer_selem_get_name(elem), index);
-
- output = (struct mixer_output_control *)calloc(1, sizeof(*output));
- if (output == NULL) {
- syslog(LOG_ERR, "No memory for output control.");
- return -ENOMEM;
- }
-
- if (snd_mixer_selem_get_playback_dB_range(elem, &min, &max) == 0) {
- output->max_volume_dB = max;
- output->min_volume_dB = min;
- }
- output->volume_curve = create_volume_curve_for_output(cmix, elem);
-
- c = &output->base;
- c->elem = elem;
- c->has_volume = snd_mixer_selem_has_playback_volume(elem);
- c->has_mute = snd_mixer_selem_has_playback_switch(elem);
- DL_APPEND(cmix->output_controls, c);
-
+ syslog(LOG_DEBUG, "Add %s control: %s,%d\n",
+ dir == CRAS_STREAM_OUTPUT ? "output" : "input",
+ name, index);
+
+ rc = mixer_control_create(&c, name, elem, dir);
+ if (rc)
+ return rc;
+
+ if (c->has_volume)
+ syslog(LOG_DEBUG, "Control '%s' volume range: [%ld:%ld]",
+ c->name, c->min_volume_dB, c->max_volume_dB);
+
+ if (dir == CRAS_STREAM_OUTPUT)
+ DL_APPEND(cmix->output_controls, c);
+ else if (dir == CRAS_STREAM_INPUT)
+ DL_APPEND(cmix->input_controls, c);
return 0;
}
-/* Adds an input control to the list. */
-static int add_input_control(struct cras_alsa_mixer *cmix,
- snd_mixer_elem_t *elem)
+static int add_control(struct cras_alsa_mixer *cmix,
+ enum CRAS_STREAM_DIRECTION dir,
+ snd_mixer_elem_t *elem)
{
- int index; /* Index part of mixer simple element */
- struct mixer_control *c;
-
- index = snd_mixer_selem_get_index(elem);
- syslog(LOG_DEBUG, "Add input control: %s,%d\n",
- snd_mixer_selem_get_name(elem), index);
-
- c = (struct mixer_control *)calloc(1, sizeof(*c));
- if (c == NULL) {
- syslog(LOG_ERR, "No memory for input control.");
- return -ENOMEM;
- }
-
- c->elem = elem;
- c->has_volume = snd_mixer_selem_has_capture_volume(elem);
- c->has_mute = snd_mixer_selem_has_capture_switch(elem);
- DL_APPEND(cmix->input_controls, c);
-
- return 0;
+ return add_control_with_name(cmix, dir, elem,
+ snd_mixer_selem_get_name(elem));
}
static void list_controls(struct mixer_control *control_list,
@@ -276,27 +573,96 @@ static struct mixer_control *get_control_matching_name(
struct mixer_control *c;
DL_FOREACH(control_list, c) {
- const char *elem_name;
-
- elem_name = snd_mixer_selem_get_name(c->elem);
- if (elem_name == NULL)
- continue;
- if (strstr(name, elem_name))
+ if (strstr(name, c->name))
return c;
}
return NULL;
}
+/* Creates a mixer_control with multiple control elements. */
+static int add_control_with_coupled_mixers(
+ struct cras_alsa_mixer *cmix,
+ enum CRAS_STREAM_DIRECTION dir,
+ const char *name,
+ struct mixer_name *coupled_controls)
+{
+ struct mixer_control *c;
+ int rc;
+
+ rc = mixer_control_create_by_name(
+ &c, cmix, name, coupled_controls, dir);
+ if (rc)
+ return rc;
+ syslog(LOG_DEBUG, "Add %s control: %s\n",
+ dir == CRAS_STREAM_OUTPUT ? "output" : "input",
+ c->name);
+ mixer_name_dump(coupled_controls, " elements");
+
+ if (c->has_volume)
+ syslog(LOG_DEBUG, "Control '%s' volume range: [%ld:%ld]",
+ c->name, c->min_volume_dB, c->max_volume_dB);
+
+ if (dir == CRAS_STREAM_OUTPUT)
+ DL_APPEND(cmix->output_controls, c);
+ else if (dir == CRAS_STREAM_INPUT)
+ DL_APPEND(cmix->input_controls, c);
+ return 0;
+}
+
+static int add_control_by_name(struct cras_alsa_mixer *cmix,
+ enum CRAS_STREAM_DIRECTION dir,
+ const char *name)
+{
+ struct mixer_control *c;
+ struct mixer_name *m_name;
+ int rc;
+
+ m_name = mixer_name_add(NULL, name, dir, MIXER_NAME_VOLUME);
+ if (!m_name)
+ return -ENOMEM;
+
+ rc = mixer_control_create_by_name(&c, cmix, name, m_name, dir);
+ mixer_name_free(m_name);
+ if (rc)
+ return rc;
+ syslog(LOG_DEBUG, "Add %s control: %s\n",
+ dir == CRAS_STREAM_OUTPUT ? "output" : "input",
+ c->name);
+
+ if (c->has_volume)
+ syslog(LOG_DEBUG, "Control '%s' volume range: [%ld:%ld]",
+ c->name, c->min_volume_dB, c->max_volume_dB);
+
+ if (dir == CRAS_STREAM_OUTPUT)
+ DL_APPEND(cmix->output_controls, c);
+ else if (dir == CRAS_STREAM_INPUT)
+ DL_APPEND(cmix->input_controls, c);
+ return 0;
+}
+
/*
* Exported interface.
*/
-struct cras_alsa_mixer *cras_alsa_mixer_create(
- const char *card_name,
- const struct cras_card_config *config,
- const char *output_names_extra[],
- size_t output_names_extra_size,
- const char *extra_main_volume)
+struct cras_alsa_mixer *cras_alsa_mixer_create(const char *card_name)
+{
+ struct cras_alsa_mixer *cmix;
+
+ cmix = (struct cras_alsa_mixer *)calloc(1, sizeof(*cmix));
+ if (cmix == NULL)
+ return NULL;
+
+ syslog(LOG_DEBUG, "Add mixer for device %s", card_name);
+
+ alsa_mixer_open(card_name, &cmix->mixer);
+
+ return cmix;
+}
+
+int cras_alsa_mixer_add_controls_by_name_matching(
+ struct cras_alsa_mixer *cmix,
+ struct mixer_name *extra_controls,
+ struct mixer_name *coupled_controls)
{
/* Names of controls for main system volume. */
static const char * const main_volume_names[] = {
@@ -321,73 +687,131 @@ struct cras_alsa_mixer *cras_alsa_mixer_create(
"Mic",
"Microphone",
};
+
+ struct mixer_name *default_controls = NULL;
snd_mixer_elem_t *elem;
- struct cras_alsa_mixer *cmix;
+ int extra_main_volume = 0;
snd_mixer_elem_t *other_elem = NULL;
long other_dB_range = 0;
+ int rc = 0;
- cmix = (struct cras_alsa_mixer *)calloc(1, sizeof(*cmix));
- if (cmix == NULL)
- return NULL;
-
- syslog(LOG_DEBUG, "Add mixer for device %s", card_name);
-
- cmix->mixer = alsa_mixer_open(card_name);
+ /* Note that there is no mixer on some cards. This is acceptable. */
if (cmix->mixer == NULL) {
syslog(LOG_DEBUG, "Couldn't open mixer.");
- free(cmix);
- return NULL;
+ return 0;
}
- cmix->config = config;
- cmix->volume_curve =
- cras_card_config_get_volume_curve_for_control(cmix->config,
- "Default");
+ default_controls = mixer_name_add_array(default_controls,
+ output_names, ARRAY_SIZE(output_names),
+ CRAS_STREAM_OUTPUT, MIXER_NAME_VOLUME);
+ default_controls = mixer_name_add_array(default_controls,
+ input_names, ARRAY_SIZE(input_names),
+ CRAS_STREAM_INPUT, MIXER_NAME_VOLUME);
+ default_controls =
+ mixer_name_add_array(default_controls,
+ main_volume_names, ARRAY_SIZE(main_volume_names),
+ CRAS_STREAM_OUTPUT, MIXER_NAME_MAIN_VOLUME);
+ default_controls =
+ mixer_name_add_array(default_controls,
+ main_capture_names, ARRAY_SIZE(main_capture_names),
+ CRAS_STREAM_INPUT, MIXER_NAME_MAIN_VOLUME);
+ extra_main_volume =
+ mixer_name_find(extra_controls, NULL,
+ CRAS_STREAM_OUTPUT,
+ MIXER_NAME_MAIN_VOLUME) != NULL;
/* Find volume and mute controls. */
for(elem = snd_mixer_first_elem(cmix->mixer);
elem != NULL; elem = snd_mixer_elem_next(elem)) {
const char *name;
+ struct mixer_name *control;
+ int found = 0;
name = snd_mixer_selem_get_name(elem);
if (name == NULL)
continue;
- if (!extra_main_volume &&
- name_in_list(name, main_volume_names,
- ARRAY_SIZE(main_volume_names))) {
- if (add_main_volume_control(cmix, elem) != 0) {
- cras_alsa_mixer_destroy(cmix);
- return NULL;
- }
- } else if (name_in_list(name, main_capture_names,
- ARRAY_SIZE(main_capture_names))) {
- if (add_main_capture_control(cmix, elem) != 0) {
- cras_alsa_mixer_destroy(cmix);
- return NULL;
+ /* Find a matching control. */
+ control = mixer_name_find(default_controls, name,
+ CRAS_STREAM_OUTPUT,
+ MIXER_NAME_UNDEFINED);
+
+ /* If our extra controls contain a main volume
+ * entry, and we found a main volume entry, then
+ * skip it. */
+ if (extra_main_volume &&
+ control && control->type == MIXER_NAME_MAIN_VOLUME)
+ control = NULL;
+
+ /* If we didn't match any of the defaults, match
+ * the extras list. */
+ if (!control)
+ control = mixer_name_find(extra_controls, name,
+ CRAS_STREAM_OUTPUT,
+ MIXER_NAME_UNDEFINED);
+
+ if (control) {
+ int rc = -1;
+ switch(control->type) {
+ case MIXER_NAME_MAIN_VOLUME:
+ rc = add_main_volume_control(cmix, elem);
+ break;
+ case MIXER_NAME_VOLUME:
+ /* TODO(dgreid) - determine device index. */
+ rc = add_control(
+ cmix, CRAS_STREAM_OUTPUT, elem);
+ break;
+ case MIXER_NAME_UNDEFINED:
+ rc = -EINVAL;
+ break;
}
- } else if (name_in_list(name, output_names,
- ARRAY_SIZE(output_names))
- || name_in_list(name, output_names_extra,
- output_names_extra_size)) {
- /* TODO(dgreid) - determine device index. */
- if (add_output_control(cmix, elem) != 0) {
- cras_alsa_mixer_destroy(cmix);
- return NULL;
+ if (rc) {
+ syslog(LOG_ERR,
+ "Failed to add mixer control '%s'"
+ " with type '%d'",
+ control->name, control->type);
+ return rc;
}
- } else if (name_in_list(name, input_names,
- ARRAY_SIZE(input_names))) {
- if (add_input_control(cmix, elem) != 0) {
- cras_alsa_mixer_destroy(cmix);
- return NULL;
+ found = 1;
+ }
+
+ /* Find a matching input control. */
+ control = mixer_name_find(default_controls, name,
+ CRAS_STREAM_INPUT,
+ MIXER_NAME_UNDEFINED);
+
+ /* If we didn't match any of the defaults, match
+ the extras list */
+ if (!control)
+ control = mixer_name_find(extra_controls, name,
+ CRAS_STREAM_INPUT,
+ MIXER_NAME_UNDEFINED);
+
+ if (control) {
+ int rc = -1;
+ switch(control->type) {
+ case MIXER_NAME_MAIN_VOLUME:
+ rc = add_main_capture_control(cmix, elem);
+ break;
+ case MIXER_NAME_VOLUME:
+ rc = add_control(
+ cmix, CRAS_STREAM_INPUT, elem);
+ break;
+ case MIXER_NAME_UNDEFINED:
+ rc = -EINVAL;
+ break;
}
- } else if (extra_main_volume &&
- !strcmp(name, extra_main_volume)) {
- if (add_main_volume_control(cmix, elem) != 0) {
- cras_alsa_mixer_destroy(cmix);
- return NULL;
+ if (rc) {
+ syslog(LOG_ERR,
+ "Failed to add mixer control '%s'"
+ " with type '%d'",
+ control->name, control->type);
+ return rc;
}
- } else if (snd_mixer_selem_has_playback_volume(elem)) {
+ found = 1;
+ }
+
+ if (!found && snd_mixer_selem_has_playback_volume(elem)) {
/* Temporarily cache one elem whose name is not
* in the list above, but has a playback volume
* control and the largest volume range. */
@@ -405,56 +829,96 @@ struct cras_alsa_mixer *cras_alsa_mixer_create(
}
}
+ /* Handle coupled output names for speaker */
+ if (coupled_controls) {
+ rc = add_control_with_coupled_mixers(
+ cmix, CRAS_STREAM_OUTPUT,
+ "Speaker", coupled_controls);
+ if (rc) {
+ syslog(LOG_ERR, "Could not add coupled output");
+ return rc;
+ }
+ }
+
/* If there is no volume control and output control found,
* use the volume control which has the largest volume range
* in the mixer as a main volume control. */
if (!cmix->main_volume_controls && !cmix->output_controls &&
other_elem) {
- if (add_main_volume_control(cmix, other_elem) != 0) {
- cras_alsa_mixer_destroy(cmix);
- return NULL;
+ rc = add_main_volume_control(cmix, other_elem);
+ if (rc) {
+ syslog(LOG_ERR, "Could not add other volume control");
+ return rc;
}
}
- return cmix;
+ return rc;
}
-void cras_alsa_mixer_destroy(struct cras_alsa_mixer *cras_mixer)
+int cras_alsa_mixer_add_controls_in_section(
+ struct cras_alsa_mixer *cmix,
+ struct ucm_section *section)
{
- struct mixer_control *c;
+ int rc;
- assert(cras_mixer);
+ /* Note that there is no mixer on some cards. This is acceptable. */
+ if (cmix->mixer == NULL) {
+ syslog(LOG_DEBUG, "Couldn't open mixer.");
+ return 0;
+ }
- DL_FOREACH(cras_mixer->main_volume_controls, c) {
- DL_DELETE(cras_mixer->main_volume_controls, c);
- free(c);
+ if (!section) {
+ syslog(LOG_ERR, "No UCM SectionDevice specified.");
+ return -EINVAL;
}
- DL_FOREACH(cras_mixer->main_capture_controls, c) {
- DL_DELETE(cras_mixer->main_capture_controls, c);
- free(c);
- }
- DL_FOREACH(cras_mixer->output_controls, c) {
- struct mixer_output_control *output;
- output = (struct mixer_output_control *)c;
- cras_volume_curve_destroy(output->volume_curve);
- DL_DELETE(cras_mixer->output_controls, c);
- free(output);
- }
- DL_FOREACH(cras_mixer->input_controls, c) {
- DL_DELETE(cras_mixer->input_controls, c);
- free(c);
- }
- cras_volume_curve_destroy(cras_mixer->volume_curve);
- snd_mixer_close(cras_mixer->mixer);
+
+ /* TODO(muirj) - Extra main volume controls when fully-specified. */
+
+ if (section->mixer_name) {
+ rc = add_control_by_name(
+ cmix, section->dir, section->mixer_name);
+ if (rc) {
+ syslog(LOG_ERR, "Could not add mixer control '%s': %s",
+ section->mixer_name, strerror(-rc));
+ return rc;
+ }
+ }
+
+ if (section->coupled) {
+ rc = add_control_with_coupled_mixers(
+ cmix, section->dir,
+ section->name, section->coupled);
+ if (rc) {
+ syslog(LOG_ERR, "Could not add coupled control: %s",
+ strerror(-rc));
+ return rc;
+ }
+ }
+ return 0;
+}
+
+void cras_alsa_mixer_destroy(struct cras_alsa_mixer *cras_mixer)
+{
+ assert(cras_mixer);
+
+ mixer_control_destroy_list(cras_mixer->main_volume_controls);
+ mixer_control_destroy_list(cras_mixer->main_capture_controls);
+ mixer_control_destroy_list(cras_mixer->output_controls);
+ mixer_control_destroy_list(cras_mixer->input_controls);
+ if (cras_mixer->mixer)
+ snd_mixer_close(cras_mixer->mixer);
free(cras_mixer);
}
-const struct cras_volume_curve *cras_alsa_mixer_default_volume_curve(
+int cras_alsa_mixer_has_main_volume(
const struct cras_alsa_mixer *cras_mixer)
{
- assert(cras_mixer);
- assert(cras_mixer->volume_curve);
- return cras_mixer->volume_curve;
+ return !!cras_mixer->main_volume_controls;
+}
+
+int cras_alsa_mixer_has_volume(const struct mixer_control *mixer_control)
+{
+ return mixer_control && mixer_control->has_volume;
}
void cras_alsa_mixer_set_dBFS(struct cras_alsa_mixer *cras_mixer,
@@ -462,8 +926,6 @@ void cras_alsa_mixer_set_dBFS(struct cras_alsa_mixer *cras_mixer,
struct mixer_control *mixer_output)
{
struct mixer_control *c;
- struct mixer_output_control *output;
- output = (struct mixer_output_control *)mixer_output;
long to_set;
assert(cras_mixer);
@@ -472,8 +934,8 @@ void cras_alsa_mixer_set_dBFS(struct cras_alsa_mixer *cras_mixer,
* combined max of the master controls and the current output.
*/
to_set = dBFS + cras_mixer->max_volume_dB;
- if (mixer_output)
- to_set += output->max_volume_dB;
+ if (cras_alsa_mixer_has_volume(mixer_output))
+ to_set += mixer_output->max_volume_dB;
/* Go through all the controls, set the volume level for each,
* taking the value closest but greater than the desired volume. If the
* entire volume can't be set on the current control, move on to the
@@ -482,17 +944,16 @@ void cras_alsa_mixer_set_dBFS(struct cras_alsa_mixer *cras_mixer,
* set to 0dB. */
DL_FOREACH(cras_mixer->main_volume_controls, c) {
long actual_dB;
- snd_mixer_selem_set_playback_dB_all(c->elem, to_set, 1);
- snd_mixer_selem_get_playback_dB(c->elem,
- SND_MIXER_SCHN_FRONT_LEFT,
- &actual_dB);
+
+ if (!c->has_volume)
+ continue;
+ mixer_control_set_dBFS(c, to_set);
+ mixer_control_get_dBFS(c, &actual_dB);
to_set -= actual_dB;
}
/* Apply the rest to the output-specific control. */
- if (mixer_output && mixer_output->elem && mixer_output->has_volume)
- snd_mixer_selem_set_playback_dB_all(mixer_output->elem,
- to_set,
- 1);
+ if (cras_alsa_mixer_has_volume(mixer_output))
+ mixer_control_set_dBFS(mixer_output, to_set);
}
long cras_alsa_mixer_get_dB_range(struct cras_alsa_mixer *cras_mixer)
@@ -505,11 +966,10 @@ long cras_alsa_mixer_get_dB_range(struct cras_alsa_mixer *cras_mixer)
long cras_alsa_mixer_get_output_dB_range(
struct mixer_control *mixer_output)
{
- struct mixer_output_control *output;
- if (!mixer_output || !mixer_output->elem || !mixer_output->has_volume)
+ if (!cras_alsa_mixer_has_volume(mixer_output))
return 0;
- output = (struct mixer_output_control *)mixer_output;
- return output->max_volume_dB - output->min_volume_dB;
+
+ return mixer_output->max_volume_dB - mixer_output->min_volume_dB;
}
void cras_alsa_mixer_set_capture_dBFS(struct cras_alsa_mixer *cras_mixer,
@@ -528,18 +988,17 @@ void cras_alsa_mixer_set_capture_dBFS(struct cras_alsa_mixer *cras_mixer,
* set the rest of the controls should be set to 0dB. */
DL_FOREACH(cras_mixer->main_capture_controls, c) {
long actual_dB;
- snd_mixer_selem_set_capture_dB_all(c->elem, to_set, 1);
- snd_mixer_selem_get_capture_dB(c->elem,
- SND_MIXER_SCHN_FRONT_LEFT,
- &actual_dB);
+
+ if (!c->has_volume)
+ continue;
+ mixer_control_set_dBFS(c, to_set);
+ mixer_control_get_dBFS(c, &actual_dB);
to_set -= actual_dB;
}
/* Apply the reset to input specific control */
- if (mixer_input && mixer_input->elem && mixer_input->has_volume)
- snd_mixer_selem_set_capture_dB_all(mixer_input->elem,
- to_set, 1);
- assert(cras_mixer);
+ if (cras_alsa_mixer_has_volume(mixer_input))
+ mixer_control_set_dBFS(mixer_input, to_set);
}
long cras_alsa_mixer_get_minimum_capture_gain(
@@ -547,38 +1006,34 @@ long cras_alsa_mixer_get_minimum_capture_gain(
struct mixer_control *mixer_input)
{
struct mixer_control *c;
- long min, max, total_min;
+ long total_min = 0;
assert(cmix);
- total_min = 0;
DL_FOREACH(cmix->main_capture_controls, c)
- if (snd_mixer_selem_get_capture_dB_range(c->elem,
- &min, &max) == 0)
- total_min += min;
-
- if (mixer_input && snd_mixer_selem_get_capture_dB_range(
- mixer_input->elem, &min, &max) == 0)
- total_min += min;
+ if (c->has_volume)
+ total_min += c->min_volume_dB;
+ if (mixer_input &&
+ mixer_input->has_volume)
+ total_min += mixer_input->min_volume_dB;
return total_min;
}
-long cras_alsa_mixer_get_maximum_capture_gain(struct cras_alsa_mixer *cmix,
+long cras_alsa_mixer_get_maximum_capture_gain(
+ struct cras_alsa_mixer *cmix,
struct mixer_control *mixer_input)
{
struct mixer_control *c;
- long min, max, total_max;
+ long total_max = 0;
assert(cmix);
- total_max = 0;
DL_FOREACH(cmix->main_capture_controls, c)
- if (snd_mixer_selem_get_capture_dB_range(c->elem,
- &min, &max) == 0)
- total_max += max;
+ if (c->has_volume)
+ total_max += c->max_volume_dB;
- if (mixer_input && snd_mixer_selem_get_capture_dB_range(
- mixer_input->elem, &min, &max) == 0)
- total_max += max;
+ if (mixer_input &&
+ mixer_input->has_volume)
+ total_max += mixer_input->max_volume_dB;
return total_max;
}
@@ -588,14 +1043,14 @@ void cras_alsa_mixer_set_mute(struct cras_alsa_mixer *cras_mixer,
struct mixer_control *mixer_output)
{
assert(cras_mixer);
+
if (cras_mixer->playback_switch) {
snd_mixer_selem_set_playback_switch_all(
cras_mixer->playback_switch, !muted);
- return;
}
- if (mixer_output && mixer_output->has_mute)
- snd_mixer_selem_set_playback_switch_all(
- mixer_output->elem, !muted);
+ if (mixer_output && mixer_output->has_mute) {
+ mixer_control_set_mute(mixer_output, muted);
+ }
}
void cras_alsa_mixer_set_capture_mute(struct cras_alsa_mixer *cras_mixer,
@@ -609,8 +1064,7 @@ void cras_alsa_mixer_set_capture_mute(struct cras_alsa_mixer *cras_mixer,
return;
}
if (mixer_input && mixer_input->has_mute)
- snd_mixer_selem_set_capture_switch_all(
- mixer_input->elem, !muted);
+ mixer_control_set_mute(mixer_input, muted);
}
void cras_alsa_mixer_list_outputs(struct cras_alsa_mixer *cras_mixer,
@@ -632,75 +1086,85 @@ void cras_alsa_mixer_list_inputs(struct cras_alsa_mixer *cras_mixer,
const char *cras_alsa_mixer_get_control_name(
const struct mixer_control *control)
{
- return snd_mixer_selem_get_name(control->elem);
-}
-
-struct mixer_control *cras_alsa_mixer_get_output_matching_name(
- const struct cras_alsa_mixer *cras_mixer,
- const char * const name)
-{
- assert(cras_mixer);
- return get_control_matching_name(cras_mixer->output_controls, name);
+ if (!control)
+ return NULL;
+ return control->name;
}
-struct mixer_control *cras_alsa_mixer_get_input_matching_name(
+struct mixer_control *cras_alsa_mixer_get_control_matching_name(
struct cras_alsa_mixer *cras_mixer,
- const char *name)
+ enum CRAS_STREAM_DIRECTION dir, const char *name,
+ int create_missing)
{
- struct mixer_control *c = NULL;
- snd_mixer_elem_t *elem;
+ struct mixer_control *c;
assert(cras_mixer);
- c = get_control_matching_name(cras_mixer->input_controls, name);
- if (c)
- return c;
+ if (!name)
+ return NULL;
- /* TODO: This is a workaround, we should pass the input names in
- * ucm config to cras_alsa_mixer_create. */
- for (elem = snd_mixer_first_elem(cras_mixer->mixer);
- elem != NULL; elem = snd_mixer_elem_next(elem)) {
- const char *control_name;
- control_name = snd_mixer_selem_get_name(elem);
+ if (dir == CRAS_STREAM_OUTPUT) {
+ c = get_control_matching_name(
+ cras_mixer->output_controls, name);
+ } else if (dir == CRAS_STREAM_INPUT) {
+ c = get_control_matching_name(
+ cras_mixer->input_controls, name);
+ } else {
+ return NULL;
+ }
+
+ /* TODO: Allowing creation of a new control is a workaround: we
+ * should pass the input names in ucm config to
+ * cras_alsa_mixer_create. */
+ if (!c && cras_mixer->mixer && create_missing) {
+ int rc = add_control_by_name(cras_mixer, dir, name);
+ if (rc)
+ return NULL;
+ c = cras_alsa_mixer_get_control_matching_name(
+ cras_mixer, dir, name, 0);
+ }
+ return c;
+}
- if (control_name == NULL)
- continue;
- if (strcmp(name, control_name) == 0) {
- if (add_input_control(cras_mixer, elem) == 0)
- return cras_mixer->input_controls->prev;
- }
+struct mixer_control *cras_alsa_mixer_get_control_for_section(
+ struct cras_alsa_mixer *cras_mixer,
+ const struct ucm_section *section)
+{
+ assert(cras_mixer && section);
+ if (section->mixer_name) {
+ return cras_alsa_mixer_get_control_matching_name(
+ cras_mixer, section->dir, section->mixer_name, 0);
+ } else if (section->coupled) {
+ return cras_alsa_mixer_get_control_matching_name(
+ cras_mixer, section->dir, section->name, 0);
}
return NULL;
}
-int cras_alsa_mixer_set_output_active_state(
- struct mixer_control *output,
- int active)
+struct mixer_control *cras_alsa_mixer_get_output_matching_name(
+ struct cras_alsa_mixer *cras_mixer,
+ const char * const name)
{
- assert(output);
- if (!output->has_mute)
- return -1;
- return snd_mixer_selem_set_playback_switch_all(output->elem, active);
+ return cras_alsa_mixer_get_control_matching_name(
+ cras_mixer, CRAS_STREAM_OUTPUT, name, 0);
}
-struct cras_volume_curve *cras_alsa_mixer_create_volume_curve_for_name(
- const struct cras_alsa_mixer *cmix,
+struct mixer_control *cras_alsa_mixer_get_input_matching_name(
+ struct cras_alsa_mixer *cras_mixer,
const char *name)
{
- if (cmix != NULL)
- return cras_card_config_get_volume_curve_for_control(
- cmix->config, name);
- else
- return cras_card_config_get_volume_curve_for_control(NULL,
- name);
+ /* TODO: Allowing creation of a new control is a workaround: we
+ * should pass the input names in ucm config to
+ * cras_alsa_mixer_create. */
+ return cras_alsa_mixer_get_control_matching_name(
+ cras_mixer, CRAS_STREAM_INPUT, name, 1);
}
-struct cras_volume_curve *cras_alsa_mixer_get_output_volume_curve(
- const struct mixer_control *control)
+int cras_alsa_mixer_set_output_active_state(
+ struct mixer_control *output,
+ int active)
{
- struct mixer_output_control *output;
- output = (struct mixer_output_control *)control;
- if (output)
- return output->volume_curve;
- else
- return NULL;
+ assert(output);
+ if (!output->has_mute)
+ return -1;
+ return mixer_control_set_mute(output, !active);
}
diff --git a/cras/src/server/cras_alsa_mixer.h b/cras/src/server/cras_alsa_mixer.h
index b2521ff0..4e0ca1ba 100644
--- a/cras/src/server/cras_alsa_mixer.h
+++ b/cras/src/server/cras_alsa_mixer.h
@@ -9,6 +9,8 @@
#include <alsa/asoundlib.h>
#include <iniparser.h>
+#include "cras_types.h"
+
/* cras_alsa_mixer represents the alsa mixer interface for an alsa card. It
* houses the volume and mute controls as well as playback switches for
* headphones and mic.
@@ -18,26 +20,43 @@ struct mixer_control;
struct cras_alsa_mixer;
struct cras_volume_curve;
struct cras_card_config;
+struct mixer_name;
+struct ucm_section;
/* Creates a cras_alsa_mixer instance for the given alsa device.
* Args:
* card_name - Name of the card to open a mixer for. This is an alsa name of
* the form "hw:X" where X ranges from 0 to 31 inclusive.
- * config - Config info for this card, can be NULL if none found.
- * output_names_extra - An array of extra output mixer control names. The
- * array may contain NULL entries which should be ignored.
- * output_names_extra_size - The length of the output_names_extra array.
- * extra_main_volume - Name of extra main volume if any.
* Returns:
* A pointer to the newly created cras_alsa_mixer which must later be freed
- * by calling cras_alsa_mixer_destroy.
+ * by calling cras_alsa_mixer_destroy. The control in the mixer is not added
+ * yet.
+ */
+struct cras_alsa_mixer *cras_alsa_mixer_create(const char *card_name);
+
+/* Adds controls to a cras_alsa_mixer from the given UCM section.
+ * Args:
+ * cmix - A pointer to cras_alsa_mixer.
+ * section - A UCM section.
+ * Returns:
+ * 0 on success. Negative error code otherwise.
+ */
+int cras_alsa_mixer_add_controls_in_section(
+ struct cras_alsa_mixer *cmix,
+ struct ucm_section *section);
+
+/* Adds controls to a cras_alsa_mixer instance by name matching.
+ * Args:
+ * cmix - A pointer to cras_alsa_mixer.
+ * extra_controls - A list array of extra mixer control names, always added.
+ * coupled_controls - A list of coupled mixer control names.
+ * Returns:
+ * 0 on success. Other error code if error happens.
*/
-struct cras_alsa_mixer *cras_alsa_mixer_create(
- const char *card_name,
- const struct cras_card_config *config,
- const char *output_names_extra[],
- size_t output_names_extra_size,
- const char *extra_main_volume);
+int cras_alsa_mixer_add_controls_by_name_matching(
+ struct cras_alsa_mixer *cmix,
+ struct mixer_name *extra_controls,
+ struct mixer_name *coupled_controls);
/* Destroys a cras_alsa_mixer that was returned from cras_alsa_mixer_create.
* Args:
@@ -46,11 +65,11 @@ struct cras_alsa_mixer *cras_alsa_mixer_create(
*/
void cras_alsa_mixer_destroy(struct cras_alsa_mixer *cras_mixer);
-/* Gets the default volume curve for this mixer. This curve will be used if
- * there is not output-node specific curve to use.
- */
-const struct cras_volume_curve *cras_alsa_mixer_default_volume_curve(
- const struct cras_alsa_mixer *mixer);
+/* Returns if the mixer has any main volume control. */
+int cras_alsa_mixer_has_main_volume(const struct cras_alsa_mixer *cras_mixer);
+
+/* Returns if the mixer control supports volume adjust. */
+int cras_alsa_mixer_has_volume(const struct mixer_control *mixer_control);
/* Sets the output volume for the device associated with this mixer.
* Args:
@@ -114,9 +133,10 @@ long cras_alsa_mixer_get_maximum_capture_gain(
/* Sets the playback switch for the device.
* Args:
- * cras_mixer - Mixer to set the volume in.
+ * cras_mixer - Mixer to set the playback switch.
* muted - 1 if muted, 0 if not.
- * mixer_output - The mixer output to mute if no master mute.
+ * mixer_output - The output specific mixer control to mute/unmute. Pass NULL
+ * to skip it.
*/
void cras_alsa_mixer_set_mute(struct cras_alsa_mixer *cras_mixer,
int muted,
@@ -154,6 +174,35 @@ void cras_alsa_mixer_list_inputs(struct cras_alsa_mixer *cras_mixer,
const char *cras_alsa_mixer_get_control_name(
const struct mixer_control *control);
+/* Returns the mixer control matching the given direction and name.
+ * Args:
+ * cras_mixer - Mixer to search for a control.
+ * dir - Control's direction (OUTPUT or INPUT).
+ * name - Name to search for.
+ * create_missing - When non-zero, attempt to create a new control with
+ * the given name.
+ * Returns:
+ * A pointer to the matching mixer control, or NULL if none found.
+ */
+struct mixer_control *cras_alsa_mixer_get_control_matching_name(
+ struct cras_alsa_mixer *cras_mixer,
+ enum CRAS_STREAM_DIRECTION dir, const char *name,
+ int create_missing);
+
+/* Returns the mixer control associated with the given section.
+ * The control is the one that matches 'mixer_name', or if that is not defined
+ * then it will be the control matching 'section->name', based on the
+ * coupled mixer controls.
+ * Args:
+ * cras_mixer - Mixer to search for a control.
+ * section - Associated UCM section.
+ * Returns:
+ * A pointer to the associated mixer control, or NULL if none found.
+ */
+struct mixer_control *cras_alsa_mixer_get_control_for_section(
+ struct cras_alsa_mixer *cras_mixer,
+ const struct ucm_section *section);
+
/* Finds the output that matches the given string. Used to match Jacks to Mixer
* elements.
* Args:
@@ -163,7 +212,7 @@ const char *cras_alsa_mixer_get_control_name(
* A pointer to the output with a mixer control that matches "name".
*/
struct mixer_control *cras_alsa_mixer_get_output_matching_name(
- const struct cras_alsa_mixer *cras_mixer,
+ struct cras_alsa_mixer *cras_mixer,
const char *name);
/* Finds the mixer control for that matches the control name of input control
@@ -183,16 +232,4 @@ int cras_alsa_mixer_set_output_active_state(
struct mixer_control *output,
int active);
-/* Returns a volume curve for the given output node name. The name can be that
- * of a control or of a Jack. Looks for an entry in the ini file (See README
- * for format), or falls back to the default volume curve if the ini file
- * doesn't specify a curve for this output. */
-struct cras_volume_curve *cras_alsa_mixer_create_volume_curve_for_name(
- const struct cras_alsa_mixer *cmix,
- const char *name);
-
-/* Returns a volume curve stored in the output control element, can be null. */
-struct cras_volume_curve *cras_alsa_mixer_get_output_volume_curve(
- const struct mixer_control *control);
-
#endif /* _CRAS_ALSA_MIXER_H */
diff --git a/cras/src/server/cras_alsa_mixer_name.c b/cras/src/server/cras_alsa_mixer_name.c
new file mode 100644
index 00000000..7f887b98
--- /dev/null
+++ b/cras/src/server/cras_alsa_mixer_name.c
@@ -0,0 +1,134 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "cras_alsa_mixer_name.h"
+#include "utlist.h"
+
+struct mixer_name *mixer_name_add(struct mixer_name *names,
+ const char *name,
+ enum CRAS_STREAM_DIRECTION dir,
+ mixer_name_type type)
+{
+ struct mixer_name *m_name;
+
+ if (!name)
+ return names;
+
+ m_name = (struct mixer_name *)calloc(1, sizeof(struct mixer_name));
+ if (!m_name)
+ return names;
+
+ m_name->name = strdup(name);
+ if (!m_name->name) {
+ free(m_name);
+ return names;
+ }
+ m_name->dir = dir;
+ m_name->type = type;
+
+ DL_APPEND(names, m_name);
+ return names;
+}
+
+struct mixer_name *mixer_name_add_array(struct mixer_name *names,
+ const char * const *name_array,
+ size_t name_array_size,
+ enum CRAS_STREAM_DIRECTION dir,
+ mixer_name_type type)
+{
+ size_t i;
+ for (i = 0; i < name_array_size; i++)
+ names = mixer_name_add(names, name_array[i], dir, type);
+ return names;
+}
+
+void mixer_name_free(struct mixer_name *names)
+{
+ struct mixer_name *m_name;
+ DL_FOREACH(names, m_name) {
+ DL_DELETE(names, m_name);
+ free((void*)m_name->name);
+ free(m_name);
+ }
+}
+
+struct mixer_name *mixer_name_find(struct mixer_name *names,
+ const char *name,
+ enum CRAS_STREAM_DIRECTION dir,
+ mixer_name_type type)
+{
+ if (!name && type == MIXER_NAME_UNDEFINED)
+ return NULL;
+
+ struct mixer_name *m_name;
+ DL_FOREACH(names, m_name) {
+ /* Match the direction. */
+ if (dir != m_name->dir)
+ continue;
+ /* Match the type unless the type is UNDEFINED. */
+ if (type != MIXER_NAME_UNDEFINED &&
+ type != m_name->type)
+ continue;
+ /* Match the name if it is non-NULL, or return the first
+ * item with the correct type when the name is not defined. */
+ if ((type != MIXER_NAME_UNDEFINED && !name) ||
+ (name && !strcmp(m_name->name, name)))
+ return m_name;
+ }
+ return NULL;
+}
+
+static const char *mixer_name_type_str(enum CRAS_STREAM_DIRECTION dir,
+ mixer_name_type type)
+{
+ switch (dir) {
+ case CRAS_STREAM_OUTPUT:
+ switch (type) {
+ case MIXER_NAME_VOLUME:
+ return "output volume";
+ case MIXER_NAME_MAIN_VOLUME:
+ return "main volume";
+ case MIXER_NAME_UNDEFINED:
+ break;
+ }
+ break;
+ case CRAS_STREAM_INPUT:
+ switch (type) {
+ case MIXER_NAME_VOLUME:
+ return "input volume";
+ case MIXER_NAME_MAIN_VOLUME:
+ return "main capture";
+ case MIXER_NAME_UNDEFINED:
+ break;
+ }
+ break;
+ case CRAS_STREAM_UNDEFINED:
+ case CRAS_STREAM_POST_MIX_PRE_DSP:
+ case CRAS_NUM_DIRECTIONS:
+ break;
+ }
+ return "undefined";
+}
+
+void mixer_name_dump(struct mixer_name *names, const char *message)
+{
+ struct mixer_name *m_name;
+
+ if (!names) {
+ syslog(LOG_DEBUG, "%s: empty", message);
+ return;
+ }
+
+ syslog(LOG_DEBUG, "%s:", message);
+ DL_FOREACH(names, m_name) {
+ const char *type_str =
+ mixer_name_type_str(m_name->dir, m_name->type);
+ syslog(LOG_DEBUG, " %s %s", m_name->name, type_str);
+ }
+}
diff --git a/cras/src/server/cras_alsa_mixer_name.h b/cras/src/server/cras_alsa_mixer_name.h
new file mode 100644
index 00000000..0a914541
--- /dev/null
+++ b/cras/src/server/cras_alsa_mixer_name.h
@@ -0,0 +1,104 @@
+/* Copyright 2016 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_ALSA_MIXER_NAME_H
+#define _CRAS_ALSA_MIXER_NAME_H
+
+#include "cras_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Type of mixer control. */
+typedef enum mixer_name_type {
+ MIXER_NAME_UNDEFINED,
+ MIXER_NAME_MAIN_VOLUME,
+ MIXER_NAME_VOLUME,
+} mixer_name_type;
+
+/* Represents a list of mixer names found in ALSA. */
+struct mixer_name {
+ const char* name;
+ enum CRAS_STREAM_DIRECTION dir;
+ mixer_name_type type;
+ struct mixer_name *prev, *next;
+};
+
+/* Add a name to the list.
+ *
+ * Args:
+ * names - A list of controls (may be NULL).
+ * name - The name to add.
+ * dir - The direction for this control.
+ * type - The type control being added.
+ *
+ * Returns:
+ * Returns the new head of the list (which changes only
+ * when names is NULL).
+ */
+struct mixer_name *mixer_name_add(struct mixer_name *names,
+ const char *name,
+ enum CRAS_STREAM_DIRECTION dir,
+ mixer_name_type type);
+
+/* Add an array of name to the list.
+ *
+ * Args:
+ * names - A list of controls (may be NULL).
+ * name_array - The names to add.
+ * name_array_size - The size of name_array.
+ * dir - The direction for these controls.
+ * type - The type controls being added.
+ *
+ * Returns:
+ * Returns the new head of the list (which changes only
+ * when names is NULL).
+ */
+struct mixer_name *mixer_name_add_array(struct mixer_name *names,
+ const char * const *name_array,
+ size_t name_array_size,
+ enum CRAS_STREAM_DIRECTION dir,
+ mixer_name_type type);
+
+/* Frees a list of names.
+ *
+ * Args:
+ * names - A list of names.
+ */
+void mixer_name_free(struct mixer_name *names);
+
+/* Find the mixer_name for the given direction, name, and type.
+ *
+ * Args:
+ * names - A list of names (may be NULL).
+ * name - The name to find, or NULL to match by type.
+
+ * dir - The direction to match.
+ * type - The type to match, or MIXER_NAME_UNDEFINED to
+ * match by name only.
+ *
+ * Returns:
+ * Returns a pointer to the matching struct mixer_name or NULL if
+ * not found.
+ */
+struct mixer_name *mixer_name_find(struct mixer_name *names,
+ const char *name,
+ enum CRAS_STREAM_DIRECTION dir,
+ mixer_name_type type);
+
+/* Dump the list of mixer names to DEBUG logs.
+ *
+ * Args:
+ * names - A list of names to dump.
+ * message - A message to print beforehand.
+ */
+void mixer_name_dump(struct mixer_name *names, const char *message);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CRAS_ALSA_MIXER_NAME_H */
diff --git a/cras/src/server/cras_alsa_ucm.c b/cras/src/server/cras_alsa_ucm.c
index c39f61c9..8aa4891a 100644
--- a/cras/src/server/cras_alsa_ucm.c
+++ b/cras/src/server/cras_alsa_ucm.c
@@ -5,12 +5,16 @@
#include <alsa/asoundlib.h>
#include <alsa/use-case.h>
+#include <ctype.h>
+#include <string.h>
#include <syslog.h>
#include "cras_alsa_ucm.h"
+#include "utlist.h"
-static const char default_verb[] = "HiFi";
static const char jack_var[] = "JackName";
+static const char jack_type_var[] = "JackType";
+static const char jack_switch_var[] = "JackSwitch";
static const char edid_var[] = "EDIDFile";
static const char cap_var[] = "CaptureControl";
static const char mic_positions[] = "MicPositions";
@@ -20,18 +24,59 @@ static const char input_dsp_name_var[] = "InputDspName";
static const char mixer_var[] = "MixerName";
static const char swap_mode_suffix[] = "Swap Mode";
static const char min_buffer_level_var[] = "MinBufferLevel";
+static const char dma_period_var[] = "DmaPeriodMicrosecs";
static const char disable_software_volume[] = "DisableSoftwareVolume";
static const char playback_device_name_var[] = "PlaybackPCM";
+static const char playback_device_rate_var[] = "PlaybackRate";
static const char capture_device_name_var[] = "CapturePCM";
+static const char capture_device_rate_var[] = "CaptureRate";
+static const char capture_channel_map_var[] = "CaptureChannelMap";
+static const char coupled_mixers[] = "CoupledMixers";
+/* Set this value in a SectionDevice to specify the maximum software gain in dBm
+ * and enable software gain on this node. */
+static const char max_software_gain[] = "MaxSoftwareGain";
+/* Set this value in a SectionDevice to specify the default node gain in dBm. */
+static const char default_node_gain[] = "DefaultNodeGain";
+static const char hotword_model_prefix[] = "Hotword Model";
+static const char fully_specified_ucm_var[] = "FullySpecifiedUCM";
+static const char main_volume_names[] = "MainVolumeNames";
+static const char enable_htimestamp_var[] = "EnableHtimestamp";
+
+/* Use case verbs corresponding to CRAS_STREAM_TYPE. */
+static const char *use_case_verbs[] = {
+ "HiFi",
+ "Multimedia",
+ "Voice Call",
+ "Speech",
+ "Pro Audio",
+};
+
+/* Represents a list of section names found in UCM. */
+struct section_name {
+ const char* name;
+ struct section_name *prev, *next;
+};
+
+struct cras_use_case_mgr {
+ snd_use_case_mgr_t *mgr;
+ const char *name;
+ unsigned int avail_use_cases;
+ enum CRAS_STREAM_TYPE use_case;
+};
+
+static inline const char *uc_verb(struct cras_use_case_mgr *mgr)
+{
+ return use_case_verbs[mgr->use_case];
+}
-static int device_enabled(snd_use_case_mgr_t *mgr, const char *dev)
+static int device_enabled(struct cras_use_case_mgr *mgr, const char *dev)
{
const char **list;
unsigned int i;
int num_devs;
int enabled = 0;
- num_devs = snd_use_case_get_list(mgr, "_enadevs", &list);
+ num_devs = snd_use_case_get_list(mgr->mgr, "_enadevs", &list);
if (num_devs <= 0)
return 0;
@@ -45,13 +90,13 @@ static int device_enabled(snd_use_case_mgr_t *mgr, const char *dev)
return enabled;
}
-static int modifier_enabled(snd_use_case_mgr_t *mgr, const char *mod)
+static int modifier_enabled(struct cras_use_case_mgr *mgr, const char *mod)
{
const char **list;
unsigned int mod_idx;
int num_mods;
- num_mods = snd_use_case_get_list(mgr, "_enamods", &list);
+ num_mods = snd_use_case_get_list(mgr->mgr, "_enamods", &list);
if (num_mods <= 0)
return 0;
@@ -63,26 +108,43 @@ static int modifier_enabled(snd_use_case_mgr_t *mgr, const char *mod)
return (mod_idx < (unsigned int)num_mods);
}
-static int get_var(snd_use_case_mgr_t *mgr, const char *var, const char *dev,
- const char *verb, const char **value)
+static int get_var(struct cras_use_case_mgr *mgr, const char *var,
+ const char *dev, const char *verb, const char **value)
{
char *id;
int rc;
+ size_t len = strlen(var) + strlen(dev) + strlen(verb) + 4;
- id = (char *)malloc(strlen(var) + strlen(dev) + strlen(verb) + 4);
+ id = (char *)malloc(len);
if (!id)
return -ENOMEM;
- sprintf(id, "=%s/%s/%s", var, dev, verb);
- rc = snd_use_case_get(mgr, id, value);
+ snprintf(id, len, "=%s/%s/%s", var, dev, verb);
+ rc = snd_use_case_get(mgr->mgr, id, value);
free((void *)id);
return rc;
}
-static int ucm_set_modifier_enabled(snd_use_case_mgr_t *mgr, const char *mod,
- int enable)
+static int get_int(struct cras_use_case_mgr *mgr, const char *var,
+ const char *dev, const char *verb, int *value)
{
- return snd_use_case_set(mgr, enable ? "_enamod" : "_dismod", mod);
+ const char *str_value;
+ int rc;
+
+ if (!value)
+ return -EINVAL;
+ rc = get_var(mgr, var, dev, verb, &str_value);
+ if (rc != 0)
+ return rc;
+ *value = atoi(str_value);
+ free((void *)str_value);
+ return 0;
+}
+
+static int ucm_set_modifier_enabled(struct cras_use_case_mgr *mgr,
+ const char *mod, int enable)
+{
+ return snd_use_case_set(mgr->mgr, enable ? "_enamod" : "_dismod", mod);
}
static int ucm_str_ends_with_suffix(const char *str, const char *suffix)
@@ -96,7 +158,7 @@ static int ucm_str_ends_with_suffix(const char *str, const char *suffix)
return strncmp(str + len_str - len_suffix, suffix, len_suffix) == 0;
}
-static int ucm_section_exists_with_name(snd_use_case_mgr_t *mgr,
+static int ucm_section_exists_with_name(struct cras_use_case_mgr *mgr,
const char *name, const char *identifier)
{
const char **list;
@@ -104,7 +166,7 @@ static int ucm_section_exists_with_name(snd_use_case_mgr_t *mgr,
int num_entries;
int exist = 0;
- num_entries = snd_use_case_get_list(mgr, identifier, &list);
+ num_entries = snd_use_case_get_list(mgr->mgr, identifier, &list);
if (num_entries <= 0)
return 0;
@@ -122,7 +184,7 @@ static int ucm_section_exists_with_name(snd_use_case_mgr_t *mgr,
return exist;
}
-static int ucm_section_exists_with_suffix(snd_use_case_mgr_t *mgr,
+static int ucm_section_exists_with_suffix(struct cras_use_case_mgr *mgr,
const char *suffix, const char *identifier)
{
const char **list;
@@ -130,7 +192,7 @@ static int ucm_section_exists_with_suffix(snd_use_case_mgr_t *mgr,
int num_entries;
int exist = 0;
- num_entries = snd_use_case_get_list(mgr, identifier, &list);
+ num_entries = snd_use_case_get_list(mgr->mgr, identifier, &list);
if (num_entries <= 0)
return 0;
@@ -148,28 +210,44 @@ static int ucm_section_exists_with_suffix(snd_use_case_mgr_t *mgr,
return exist;
}
-static int ucm_mod_exists_with_suffix(snd_use_case_mgr_t *mgr,
- const char *suffix)
+static int ucm_mod_exists_with_suffix(struct cras_use_case_mgr *mgr,
+ const char *suffix)
{
- return ucm_section_exists_with_suffix(mgr, suffix, "_modifiers/HiFi");
+ char *identifier;
+ int rc;
+
+ identifier = snd_use_case_identifier("_modifiers/%s", uc_verb(mgr));
+ rc = ucm_section_exists_with_suffix(mgr, suffix, identifier);
+ free(identifier);
+ return rc;
}
-static int ucm_mod_exists_with_name(snd_use_case_mgr_t *mgr, const char *name)
+static int ucm_mod_exists_with_name(struct cras_use_case_mgr *mgr,
+ const char *name)
{
- return ucm_section_exists_with_name(mgr, name, "_modifiers/HiFi");
+ char *identifier;
+ int rc;
+
+ identifier = snd_use_case_identifier("_modifiers/%s", uc_verb(mgr));
+ rc = ucm_section_exists_with_name(mgr, name, identifier);
+ free(identifier);
+ return rc;
}
-static char *ucm_get_section_for_var(snd_use_case_mgr_t *mgr, const char *var,
- const char *value, const char *identifier,
- enum CRAS_STREAM_DIRECTION direction)
+/* Get a list of section names whose variable is the matched value. */
+static struct section_name * ucm_get_sections_for_var(
+ struct cras_use_case_mgr *mgr,
+ const char *var, const char *value,
+ const char *identifier,
+ enum CRAS_STREAM_DIRECTION direction)
{
const char **list;
- char *section_name = NULL;
+ struct section_name *section_names = NULL, *s_name;
unsigned int i;
int num_entries;
int rc;
- num_entries = snd_use_case_get_list(mgr, identifier, &list);
+ num_entries = snd_use_case_get_list(mgr->mgr, identifier, &list);
if (num_entries <= 0)
return NULL;
@@ -181,43 +259,52 @@ static char *ucm_get_section_for_var(snd_use_case_mgr_t *mgr, const char *var,
if (!list[i])
continue;
- /* Skip mic seciton for output, only check mic for input. */
- if (!strcmp(list[i], "Mic")) {
- if (direction == CRAS_STREAM_OUTPUT)
- continue;
- } else {
- if (direction == CRAS_STREAM_INPUT)
- continue;
- }
-
- rc = get_var(mgr, var, list[i], default_verb, &this_value);
+ rc = get_var(mgr, var, list[i], uc_verb(mgr), &this_value);
if (rc)
continue;
if (!strcmp(value, this_value)) {
- section_name = strdup(list[i]);
- free((void *)this_value);
- break;
+ s_name = (struct section_name *)malloc(
+ sizeof(struct section_name));
+
+ if (!s_name) {
+ syslog(LOG_ERR, "Failed to allocate memory");
+ free((void *)this_value);
+ break;
+ }
+
+ s_name->name = strdup(list[i]);
+ DL_APPEND(section_names, s_name);
}
free((void *)this_value);
}
snd_use_case_free_list(list, num_entries);
- return section_name;
+ return section_names;
}
-static char *ucm_get_dev_for_var(snd_use_case_mgr_t *mgr, const char *var,
- const char *value, enum CRAS_STREAM_DIRECTION dir) {
- return ucm_get_section_for_var(mgr, var, value, "_devices/HiFi", dir);
+static struct section_name *ucm_get_devices_for_var(
+ struct cras_use_case_mgr *mgr,
+ const char *var, const char *value,
+ enum CRAS_STREAM_DIRECTION dir)
+{
+ char *identifier;
+ struct section_name *section_names;
+
+ identifier = snd_use_case_identifier("_devices/%s", uc_verb(mgr));
+ section_names = ucm_get_sections_for_var(mgr, var, value, identifier,
+ dir);
+ free(identifier);
+ return section_names;
}
static const char *ucm_get_playback_device_name_for_dev(
- snd_use_case_mgr_t *mgr, const char *dev)
+ struct cras_use_case_mgr *mgr, const char *dev)
{
const char *name = NULL;
int rc;
- rc = get_var(mgr, playback_device_name_var, dev, default_verb, &name);
+ rc = get_var(mgr, playback_device_name_var, dev, uc_verb(mgr), &name);
if (rc)
return NULL;
@@ -225,66 +312,140 @@ static const char *ucm_get_playback_device_name_for_dev(
}
static const char *ucm_get_capture_device_name_for_dev(
- snd_use_case_mgr_t *mgr, const char *dev)
+ struct cras_use_case_mgr *mgr, const char *dev)
{
const char *name = NULL;
int rc;
- rc = get_var(mgr, capture_device_name_var, dev, default_verb, &name);
+ rc = get_var(mgr, capture_device_name_var, dev, uc_verb(mgr), &name);
if (rc)
return NULL;
return name;
}
+/* Get a list of mixer names specified in a UCM variable separated by ",".
+ * E.g. "Left Playback,Right Playback".
+ */
+static struct mixer_name *ucm_get_mixer_names(struct cras_use_case_mgr *mgr,
+ const char *dev, const char* var,
+ enum CRAS_STREAM_DIRECTION dir,
+ mixer_name_type type)
+{
+ const char *names_in_string = NULL;
+ int rc;
+ char *tokens, *name, *laststr;
+ struct mixer_name *names = NULL;
+
+ rc = get_var(mgr, var, dev, uc_verb(mgr), &names_in_string);
+ if (rc)
+ return NULL;
+
+ tokens = strdup(names_in_string);
+ name = strtok_r(tokens, ",", &laststr);
+ while (name != NULL) {
+ names = mixer_name_add(names, name, dir, type);
+ name = strtok_r(NULL, ",", &laststr);
+ }
+ free((void*)names_in_string);
+ free(tokens);
+ return names;
+}
+
/* Exported Interface */
-snd_use_case_mgr_t *ucm_create(const char *name)
+struct cras_use_case_mgr *ucm_create(const char *name)
{
- snd_use_case_mgr_t *mgr;
+ struct cras_use_case_mgr *mgr;
int rc;
+ const char **list;
+ int num_verbs, i, j;
if (!name)
return NULL;
- rc = snd_use_case_mgr_open(&mgr, name);
+ mgr = (struct cras_use_case_mgr *)malloc(sizeof(*mgr));
+ if (!mgr)
+ return NULL;
+
+ rc = snd_use_case_mgr_open(&mgr->mgr, name);
if (rc) {
- syslog(LOG_ERR, "Can not open ucm for card %s, rc = %d",
+ syslog(LOG_WARNING, "Can not open ucm for card %s, rc = %d",
name, rc);
- return NULL;
+ goto cleanup;
}
- rc = snd_use_case_set(mgr, "_verb", default_verb);
- if (rc) {
- syslog(LOG_ERR, "Can not set verb %s for card %s, rc = %d",
- default_verb, name, rc);
- ucm_destroy(mgr);
- return NULL;
+ mgr->name = name;
+ mgr->avail_use_cases = 0;
+ num_verbs = snd_use_case_get_list(mgr->mgr, "_verbs", &list);
+ for (i = 0; i < num_verbs; i += 2) {
+ for (j = 0; j < CRAS_STREAM_NUM_TYPES; ++j) {
+ if (strcmp(list[i], use_case_verbs[j]) == 0)
+ break;
+ }
+ if (j < CRAS_STREAM_NUM_TYPES)
+ mgr->avail_use_cases |= (1 << j);
}
+ if (num_verbs > 0)
+ snd_use_case_free_list(list, num_verbs);
+
+ rc = ucm_set_use_case(mgr, CRAS_STREAM_TYPE_DEFAULT);
+ if (rc)
+ goto cleanup_mgr;
return mgr;
+
+cleanup_mgr:
+ snd_use_case_mgr_close(mgr->mgr);
+cleanup:
+ free(mgr);
+ return NULL;
}
-void ucm_destroy(snd_use_case_mgr_t *mgr)
+void ucm_destroy(struct cras_use_case_mgr *mgr)
{
- snd_use_case_mgr_close(mgr);
+ snd_use_case_mgr_close(mgr->mgr);
+ free(mgr);
}
-int ucm_swap_mode_exists(snd_use_case_mgr_t *mgr)
+int ucm_set_use_case(struct cras_use_case_mgr *mgr,
+ enum CRAS_STREAM_TYPE use_case)
+{
+ int rc;
+
+ if (mgr->avail_use_cases & (1 << use_case)) {
+ mgr->use_case = use_case;
+ } else {
+ syslog(LOG_ERR, "Unavailable use case %d for card %s",
+ use_case, mgr->name);
+ return -1;
+ }
+
+ rc = snd_use_case_set(mgr->mgr, "_verb", uc_verb(mgr));
+ if (rc) {
+ syslog(LOG_ERR, "Can not set verb %s for card %s, rc = %d",
+ uc_verb(mgr), mgr->name, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+int ucm_swap_mode_exists(struct cras_use_case_mgr *mgr)
{
return ucm_mod_exists_with_suffix(mgr, swap_mode_suffix);
}
-int ucm_enable_swap_mode(snd_use_case_mgr_t *mgr, const char *node_name,
- int enable)
+int ucm_enable_swap_mode(struct cras_use_case_mgr *mgr, const char *node_name,
+ int enable)
{
char *swap_mod = NULL;
int rc;
- swap_mod = (char *)malloc(strlen(node_name) + 1 +
- strlen(swap_mode_suffix) + 1);
+ size_t len = strlen(node_name) + 1 + strlen(swap_mode_suffix) + 1;
+ swap_mod = (char *)malloc(len);
if (!swap_mod)
return -ENOMEM;
- sprintf(swap_mod, "%s %s", node_name, swap_mode_suffix);
+ snprintf(swap_mod, len, "%s %s", node_name, swap_mode_suffix);
if (!ucm_mod_exists_with_name(mgr, swap_mod)) {
syslog(LOG_ERR, "Can not find swap mode modifier %s.", swap_mod);
free((void *)swap_mod);
@@ -299,22 +460,22 @@ int ucm_enable_swap_mode(snd_use_case_mgr_t *mgr, const char *node_name,
return rc;
}
-int ucm_set_enabled(snd_use_case_mgr_t *mgr, const char *dev, int enable)
+int ucm_set_enabled(struct cras_use_case_mgr *mgr, const char *dev, int enable)
{
if (device_enabled(mgr, dev) == !!enable)
return 0;
-
- return snd_use_case_set(mgr, enable ? "_enadev" : "_disdev", dev);
+ syslog(LOG_DEBUG, "UCM %s %s", enable ? "enable" : "disable", dev);
+ return snd_use_case_set(mgr->mgr, enable ? "_enadev" : "_disdev", dev);
}
-char *ucm_get_flag(snd_use_case_mgr_t *mgr, const char *flag_name)
+char *ucm_get_flag(struct cras_use_case_mgr *mgr, const char *flag_name)
{
char *flag_value = NULL;
const char *value;
int rc;
/* Set device to empty string since flag is specified in verb section */
- rc = get_var(mgr, flag_name, "", default_verb, &value);
+ rc = get_var(mgr, flag_name, "", uc_verb(mgr), &value);
if (!rc) {
flag_value = strdup(value);
free((void *)value);
@@ -323,13 +484,13 @@ char *ucm_get_flag(snd_use_case_mgr_t *mgr, const char *flag_name)
return flag_value;
}
-char *ucm_get_cap_control(snd_use_case_mgr_t *mgr, const char *ucm_dev)
+char *ucm_get_cap_control(struct cras_use_case_mgr *mgr, const char *ucm_dev)
{
char *control_name = NULL;
const char *value;
int rc;
- rc = get_var(mgr, cap_var, ucm_dev, default_verb, &value);
+ rc = get_var(mgr, cap_var, ucm_dev, uc_verb(mgr), &value);
if (!rc) {
control_name = strdup(value);
free((void *)value);
@@ -338,13 +499,13 @@ char *ucm_get_cap_control(snd_use_case_mgr_t *mgr, const char *ucm_dev)
return control_name;
}
-char *ucm_get_mic_positions(snd_use_case_mgr_t *mgr)
+char *ucm_get_mic_positions(struct cras_use_case_mgr *mgr)
{
char *control_name = NULL;
const char *value;
int rc;
- rc = get_var(mgr, mic_positions, "", default_verb, &value);
+ rc = get_var(mgr, mic_positions, "", uc_verb(mgr), &value);
if (!rc) {
control_name = strdup(value);
free((void *)value);
@@ -353,13 +514,13 @@ char *ucm_get_mic_positions(snd_use_case_mgr_t *mgr)
return control_name;
}
-const char *ucm_get_override_type_name(snd_use_case_mgr_t *mgr,
- const char *dev)
+const char *ucm_get_override_type_name(struct cras_use_case_mgr *mgr,
+ const char *dev)
{
const char *override_type_name;
int rc;
- rc = get_var(mgr, override_type_name_var, dev, default_verb,
+ rc = get_var(mgr, override_type_name_var, dev, uc_verb(mgr),
&override_type_name);
if (rc)
return NULL;
@@ -367,31 +528,71 @@ const char *ucm_get_override_type_name(snd_use_case_mgr_t *mgr,
return override_type_name;
}
-char *ucm_get_dev_for_jack(snd_use_case_mgr_t *mgr, const char *jack,
+char *ucm_get_dev_for_jack(struct cras_use_case_mgr *mgr, const char *jack,
enum CRAS_STREAM_DIRECTION direction)
{
- return ucm_get_dev_for_var(mgr, jack_var, jack, direction);
+ struct section_name *section_names, *c;
+ char *ret = NULL;
+
+ section_names = ucm_get_devices_for_var(mgr, jack_var, jack, direction);
+
+ DL_FOREACH(section_names, c) {
+ if (!strcmp(c->name, "Mic")) {
+ /* Skip mic section for output */
+ if (direction == CRAS_STREAM_OUTPUT)
+ continue;
+ } else {
+ /* Only check mic for input. */
+ if (direction == CRAS_STREAM_INPUT)
+ continue;
+ }
+ ret = strdup(c->name);
+ break;
+ }
+
+ DL_FOREACH(section_names, c) {
+ DL_DELETE(section_names, c);
+ free((void*)c->name);
+ free(c);
+ }
+
+ return ret;
}
-char *ucm_get_dev_for_mixer(snd_use_case_mgr_t *mgr, const char *mixer,
+char *ucm_get_dev_for_mixer(struct cras_use_case_mgr *mgr, const char *mixer,
enum CRAS_STREAM_DIRECTION dir)
{
- return ucm_get_dev_for_var(mgr, mixer_var, mixer, dir);
+ struct section_name *section_names, *c;
+ char *ret = NULL;
+
+ section_names = ucm_get_devices_for_var(mgr, mixer_var, mixer, dir);
+
+ if (section_names)
+ ret = strdup(section_names->name);
+
+ DL_FOREACH(section_names, c) {
+ DL_DELETE(section_names, c);
+ free((void*)c->name);
+ free(c);
+ }
+
+ return ret;
}
-const char *ucm_get_edid_file_for_dev(snd_use_case_mgr_t *mgr, const char *dev)
+const char *ucm_get_edid_file_for_dev(struct cras_use_case_mgr *mgr,
+ const char *dev)
{
const char *file_name;
int rc;
- rc = get_var(mgr, edid_var, dev, default_verb, &file_name);
+ rc = get_var(mgr, edid_var, dev, uc_verb(mgr), &file_name);
if (rc)
return NULL;
return file_name;
}
-const char *ucm_get_dsp_name(snd_use_case_mgr_t *mgr, const char *ucm_dev,
+const char *ucm_get_dsp_name(struct cras_use_case_mgr *mgr, const char *ucm_dev,
int direction)
{
const char *var = (direction == CRAS_STREAM_OUTPUT)
@@ -400,44 +601,71 @@ const char *ucm_get_dsp_name(snd_use_case_mgr_t *mgr, const char *ucm_dev,
const char *dsp_name = NULL;
int rc;
- rc = get_var(mgr, var, ucm_dev, default_verb, &dsp_name);
+ rc = get_var(mgr, var, ucm_dev, uc_verb(mgr), &dsp_name);
if (rc)
return NULL;
return dsp_name;
}
-const char *ucm_get_dsp_name_default(snd_use_case_mgr_t *mgr, int direction)
+const char *ucm_get_dsp_name_default(struct cras_use_case_mgr *mgr,
+ int direction)
{
return ucm_get_dsp_name(mgr, "", direction);
}
-unsigned int ucm_get_min_buffer_level(snd_use_case_mgr_t *mgr)
+unsigned int ucm_get_min_buffer_level(struct cras_use_case_mgr *mgr)
{
- const char *val = NULL;
+ int value;
int rc;
- rc = get_var(mgr, min_buffer_level_var, "", default_verb, &val);
+ rc = get_int(mgr, min_buffer_level_var, "", uc_verb(mgr), &value);
if (rc)
return 0;
- return atoi(val);
+ return value;
}
-unsigned int ucm_get_disable_software_volume(snd_use_case_mgr_t *mgr)
+unsigned int ucm_get_disable_software_volume(struct cras_use_case_mgr *mgr)
{
- const char *val = NULL;
+ int value;
int rc;
- rc = get_var(mgr, disable_software_volume, "", default_verb, &val);
+ rc = get_int(mgr, disable_software_volume, "", uc_verb(mgr), &value);
if (rc)
return 0;
- return atoi(val);
+ return value;
+}
+
+int ucm_get_max_software_gain(struct cras_use_case_mgr *mgr, const char *dev,
+ long *gain)
+{
+ int value;
+ int rc;
+
+ rc = get_int(mgr, max_software_gain, dev, uc_verb(mgr), &value);
+ if (rc)
+ return rc;
+ *gain = value;
+ return 0;
+}
+
+int ucm_get_default_node_gain(struct cras_use_case_mgr *mgr, const char *dev,
+ long *gain)
+{
+ int value;
+ int rc;
+
+ rc = get_int(mgr, default_node_gain, dev, uc_verb(mgr), &value);
+ if (rc)
+ return rc;
+ *gain = value;
+ return 0;
}
const char *ucm_get_device_name_for_dev(
- snd_use_case_mgr_t *mgr, const char *dev,
+ struct cras_use_case_mgr *mgr, const char *dev,
enum CRAS_STREAM_DIRECTION direction)
{
if (direction == CRAS_STREAM_OUTPUT)
@@ -446,3 +674,384 @@ const char *ucm_get_device_name_for_dev(
return ucm_get_capture_device_name_for_dev(mgr, dev);
return NULL;
}
+
+int ucm_get_sample_rate_for_dev(struct cras_use_case_mgr *mgr, const char *dev,
+ enum CRAS_STREAM_DIRECTION direction)
+{
+ int value;
+ int rc;
+ const char *var_name;
+
+ if (direction == CRAS_STREAM_OUTPUT)
+ var_name = playback_device_rate_var;
+ else if (direction == CRAS_STREAM_INPUT)
+ var_name = capture_device_rate_var;
+ else
+ return -EINVAL;
+
+ rc = get_int(mgr, var_name, dev, uc_verb(mgr), &value);
+ if (rc)
+ return rc;
+
+ return value;
+}
+
+int ucm_get_capture_chmap_for_dev(struct cras_use_case_mgr *mgr,
+ const char *dev,
+ int8_t *channel_layout)
+{
+ const char *var_str;
+ char *tokens, *token;
+ int i, rc;
+
+ rc = get_var(mgr, capture_channel_map_var, dev, uc_verb(mgr), &var_str);
+ if (rc)
+ return rc;
+
+ tokens = strdup(var_str);
+ token = strtok(tokens, " ");
+ for (i = 0; token && (i < CRAS_CH_MAX); i++) {
+ channel_layout[i] = atoi(token);
+ token = strtok(NULL, " ");
+ }
+
+ free((void *)tokens);
+ free((void *)var_str);
+ return (i == CRAS_CH_MAX) ? 0 : -EINVAL;
+}
+
+struct mixer_name *ucm_get_coupled_mixer_names(
+ struct cras_use_case_mgr *mgr, const char *dev)
+{
+ return ucm_get_mixer_names(mgr, dev, coupled_mixers,
+ CRAS_STREAM_OUTPUT,
+ MIXER_NAME_VOLUME);
+}
+
+static int get_device_index_from_target(const char *target_device_name)
+{
+ /* Expects a string in the form: hw:card-name,<num> */
+ const char *pos = target_device_name;
+ if (!pos)
+ return -1;
+ while (*pos && *pos != ',')
+ ++pos;
+ if (*pos == ',') {
+ ++pos;
+ return atoi(pos);
+ }
+ return -1;
+}
+
+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;
+ char *identifier;
+
+ /* Find the list of all mixers using the control names defined in
+ * the header definintion for this function. */
+ identifier = snd_use_case_identifier("_devices/%s", uc_verb(mgr));
+ num_devs = snd_use_case_get_list(mgr->mgr, identifier, &list);
+ free(identifier);
+
+ /* snd_use_case_get_list fills list with pairs of device name and
+ * comment, so device names are in even-indexed elements. */
+ for (i = 0; i < num_devs; i += 2) {
+ enum CRAS_STREAM_DIRECTION dir = CRAS_STREAM_UNDEFINED;
+ int dev_idx = -1;
+ const char *dev_name = strdup(list[i]);
+ const char *jack_name;
+ const char *jack_type;
+ const char *mixer_name;
+ struct mixer_name *m_name;
+ int rc;
+ const char *target_device_name;
+
+ if (!dev_name)
+ continue;
+
+ target_device_name =
+ ucm_get_playback_device_name_for_dev(mgr, dev_name);
+ if (target_device_name)
+ dir = CRAS_STREAM_OUTPUT;
+ else {
+ target_device_name =
+ ucm_get_capture_device_name_for_dev(
+ mgr, dev_name);
+ if (target_device_name)
+ dir = CRAS_STREAM_INPUT;
+ }
+ if (target_device_name) {
+ dev_idx = get_device_index_from_target(
+ target_device_name);
+ free((void *)target_device_name);
+ }
+
+ if (dir == CRAS_STREAM_UNDEFINED) {
+ syslog(LOG_ERR,
+ "UCM configuration for device '%s' missing"
+ " PlaybackPCM or CapturePCM definition.",
+ dev_name);
+ goto error_cleanup;
+ }
+
+ if (dev_idx == -1) {
+ syslog(LOG_ERR,
+ "PlaybackPCM or CapturePCM for '%s' must be in"
+ " the form 'hw:<card>,<number>'", dev_name);
+ goto error_cleanup;
+ }
+
+ jack_name = ucm_get_jack_name_for_dev(mgr, dev_name);
+ jack_type = ucm_get_jack_type_for_dev(mgr, dev_name);
+ mixer_name = ucm_get_mixer_name_for_dev(mgr, dev_name);
+
+ dev_sec = ucm_section_create(dev_name, dev_idx, dir,
+ jack_name, jack_type);
+ if (jack_name)
+ free((void *)jack_name);
+ if (jack_type)
+ free((void *)jack_type);
+
+ 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);
+ }
+
+ 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);
+ return NULL;
+}
+
+char *ucm_get_hotword_models(struct cras_use_case_mgr *mgr)
+{
+ const char **list;
+ int i, num_entries;
+ int models_len = 0;
+ char *models = NULL;
+ const char *tmp;
+ char *identifier;
+
+ identifier = snd_use_case_identifier("_modifiers/%s", uc_verb(mgr));
+ num_entries = snd_use_case_get_list(mgr->mgr, identifier, &list);
+ free(identifier);
+ if (num_entries <= 0)
+ return 0;
+ models = (char *)malloc(num_entries * 8);
+ for (i = 0; i < num_entries; i+=2) {
+ if (!list[i])
+ continue;
+ if (0 == strncmp(list[i], hotword_model_prefix,
+ strlen(hotword_model_prefix))) {
+ tmp = list[i] + strlen(hotword_model_prefix);
+ while (isspace(*tmp))
+ tmp++;
+ strcpy(models + models_len, tmp);
+ models_len += strlen(tmp);
+ if (i + 2 >= num_entries)
+ models[models_len] = '\0';
+ else
+ models[models_len++] = ',';
+ }
+ }
+ snd_use_case_free_list(list, num_entries);
+
+ return models;
+}
+
+int ucm_set_hotword_model(struct cras_use_case_mgr *mgr, const char *model)
+{
+ const char **list;
+ int num_enmods, mod_idx;
+ char *model_mod = NULL;
+ size_t model_mod_size = strlen(model) + 1 +
+ strlen(hotword_model_prefix) + 1;
+ model_mod = (char *)malloc(model_mod_size);
+ if (!model_mod)
+ return -ENOMEM;
+ snprintf(model_mod, model_mod_size,
+ "%s %s", hotword_model_prefix, model);
+ if (!ucm_mod_exists_with_name(mgr, model_mod)) {
+ free((void *)model_mod);
+ return -EINVAL;
+ }
+
+ /* Disable all currently enabled horword model modifiers. */
+ num_enmods = snd_use_case_get_list(mgr->mgr, "_enamods", &list);
+ if (num_enmods <= 0)
+ goto enable_mod;
+
+ for (mod_idx = 0; mod_idx < num_enmods; mod_idx++) {
+ if (!strncmp(list[mod_idx], hotword_model_prefix,
+ strlen(hotword_model_prefix)))
+ ucm_set_modifier_enabled(mgr, list[mod_idx], 0);
+ }
+ snd_use_case_free_list(list, num_enmods);
+
+enable_mod:
+ ucm_set_modifier_enabled(mgr, model_mod, 1);
+
+ return 0;
+}
+
+int ucm_has_fully_specified_ucm_flag(struct cras_use_case_mgr *mgr)
+{
+ char *flag;
+ int ret = 0;
+ flag = ucm_get_flag(mgr, fully_specified_ucm_var);
+ if (!flag)
+ return 0;
+ ret = !strcmp(flag, "1");
+ free(flag);
+ return ret;
+}
+
+const char *ucm_get_mixer_name_for_dev(struct cras_use_case_mgr *mgr, const char *dev)
+{
+ const char *name = NULL;
+ int rc;
+
+ rc = get_var(mgr, mixer_var, dev, uc_verb(mgr), &name);
+ if (rc)
+ return NULL;
+
+ return name;
+}
+
+struct mixer_name *ucm_get_main_volume_names(struct cras_use_case_mgr *mgr)
+{
+ return ucm_get_mixer_names(mgr, "", main_volume_names,
+ CRAS_STREAM_OUTPUT, MIXER_NAME_MAIN_VOLUME);
+}
+
+int ucm_list_section_devices_by_device_name(
+ struct cras_use_case_mgr *mgr,
+ enum CRAS_STREAM_DIRECTION direction,
+ const char *device_name,
+ ucm_list_section_devices_callback cb,
+ void *cb_arg)
+{
+ int listed= 0;
+ struct section_name *section_names, *c;
+ const char* var;
+ char *identifier;
+
+ if (direction == CRAS_STREAM_OUTPUT)
+ var = playback_device_name_var;
+ else if (direction == CRAS_STREAM_INPUT)
+ var = capture_device_name_var;
+ else
+ return 0;
+
+ identifier = snd_use_case_identifier("_devices/%s", uc_verb(mgr));
+ section_names = ucm_get_sections_for_var(
+ mgr, var, device_name, identifier, direction);
+ free(identifier);
+ if (!section_names)
+ return 0;
+
+ DL_FOREACH(section_names, c) {
+ cb(c->name, cb_arg);
+ listed++;
+ }
+
+ DL_FOREACH(section_names, c) {
+ DL_DELETE(section_names, c);
+ free((void*)c->name);
+ free(c);
+ }
+ return listed;
+}
+
+const char *ucm_get_jack_name_for_dev(struct cras_use_case_mgr *mgr,
+ const char *dev)
+{
+ const char *name = NULL;
+ int rc;
+
+ rc = get_var(mgr, jack_var, dev, uc_verb(mgr), &name);
+ if (rc)
+ return NULL;
+
+ return name;
+}
+
+const char *ucm_get_jack_type_for_dev(struct cras_use_case_mgr *mgr,
+ const char *dev)
+{
+ const char *name = NULL;
+ int rc;
+
+ rc = get_var(mgr, jack_type_var, dev, uc_verb(mgr), &name);
+ if (rc)
+ return NULL;
+
+ if (strcmp(name, "hctl") && strcmp(name, "gpio")) {
+ syslog(LOG_ERR, "Unknown jack type: %s", name);
+ return NULL;
+ }
+ return name;
+}
+
+int ucm_get_jack_switch_for_dev(struct cras_use_case_mgr *mgr, const char *dev)
+{
+ int value;
+
+ int rc = get_int(mgr, jack_switch_var, dev, uc_verb(mgr), &value);
+ if (rc || value < 0)
+ return -1;
+ return value;
+}
+
+unsigned int ucm_get_dma_period_for_dev(struct cras_use_case_mgr *mgr,
+ const char *dev)
+{
+ int value;
+
+ int rc = get_int(mgr, dma_period_var, dev, uc_verb(mgr), &value);
+ if (rc || value < 0)
+ return 0;
+ return value;
+}
+
+unsigned int ucm_get_enable_htimestamp_flag(struct cras_use_case_mgr *mgr)
+{
+ char *flag;
+ int ret = 0;
+ flag = ucm_get_flag(mgr, enable_htimestamp_var);
+ if (!flag)
+ return 0;
+ ret = !strcmp(flag, "1");
+ free(flag);
+ return ret;
+}
diff --git a/cras/src/server/cras_alsa_ucm.h b/cras/src/server/cras_alsa_ucm.h
index 97bad128..622bbf85 100644
--- a/cras/src/server/cras_alsa_ucm.h
+++ b/cras/src/server/cras_alsa_ucm.h
@@ -7,16 +7,19 @@
#define _CRAS_ALSA_UCM_H
#include <alsa/asoundlib.h>
-#include <alsa/use-case.h>
+#include "cras_alsa_mixer_name.h"
+#include "cras_alsa_ucm_section.h"
#include "cras_types.h"
+struct cras_use_case_mgr;
+
/* Helpers to access UCM configuration for a card if any is provided.
* This configuration can specify how to enable or disable certain inputs and
* outputs on the card.
*/
-/* Creates a snd_use_case_mgr_t instance for the given card name if there is a
+/* Creates a cras_use_case_mgr instance for the given card name if there is a
* matching ucm configuration. It there is a matching UCM config, then it will
* be configured to the default state.
*
@@ -24,163 +27,198 @@
* name - Name of the card to match against the UCM card list.
* Returns:
* A pointer to the use case manager if found, otherwise NULL. The pointer
- * must later be freed with snd_use_case_mgr_close().
+ * must later be freed with ucm_destroy().
*/
-snd_use_case_mgr_t *ucm_create(const char *name);
+struct cras_use_case_mgr *ucm_create(const char *name);
+
-/* Destroys a snd_use_case_mgr_t that was returned from ucm_create.
+/* Destroys a cras_use_case_mgr that was returned from ucm_create.
* Args:
- * alsa_ucm - The snd_use_case_mgr_t pointer returned from alsa_ucm_create.
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ */
+void ucm_destroy(struct cras_use_case_mgr *mgr);
+
+/* Sets the new use case for the given cras_use_case_mgr.
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from ucm_create.
+ * use_case - The new use case to be set.
+ * Returns:
+ * 0 on success or negative error code on failure.
*/
-void ucm_destroy(snd_use_case_mgr_t *mgr);
+int ucm_set_use_case(struct cras_use_case_mgr *mgr,
+ enum CRAS_STREAM_TYPE use_case);
/* Checks if modifier for left right swap mode exists in ucm.
* Args:
- * alsa_ucm - The snd_use_case_mgr_t pointer returned from alsa_ucm_create.
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
* Returns:
* 1 if it exists, 0 otherwise.
*/
-int ucm_swap_mode_exists(snd_use_case_mgr_t *mgr);
+int ucm_swap_mode_exists(struct cras_use_case_mgr *mgr);
/* Enables or disables swap mode for the given node_name. First checks
* if the modifier is already enabled or disabled.
* Args:
- * alsa_ucm - The snd_use_case_mgr_t pointer returned from alsa_ucm_create.
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
* node_name - The node name.
* enable - Enable device if non-zero.
* Returns:
* 0 on success or negative error code on failure.
*/
-int ucm_enable_swap_mode(snd_use_case_mgr_t *mgr, const char *node_name,
- int enable);
+int ucm_enable_swap_mode(struct cras_use_case_mgr *mgr, const char *node_name,
+ int enable);
/* Enables or disables a UCM device. First checks if the device is already
* enabled or disabled.
* Args:
- * alsa_ucm - The snd_use_case_mgr_t pointer returned from alsa_ucm_create.
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
* dev - The ucm device to enable of disable.
* enable - Enable device if non-zero.
* Returns:
* 0 on success or negative error code on failure.
*/
-int ucm_set_enabled(snd_use_case_mgr_t *mgr, const char *dev, int enable);
+int ucm_set_enabled(struct cras_use_case_mgr *mgr, const char *dev, int enable);
/* Gets the value of given flag name.
* Args:
- * mgr - The snd_use_case_mgr_t pointer returned from alsa_ucm_create.
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
* flag_name - The name of the flag.
* Returns:
* A pointer to the allocated string containing the flag value, or
* NULL if the flag is not set.
*/
-char *ucm_get_flag(snd_use_case_mgr_t *mgr, const char *flag_name);
+char *ucm_get_flag(struct cras_use_case_mgr *mgr, const char *flag_name);
/* Gets the capture control name which associated with given ucm device.
* Args:
- * mgr - The snd_use_case_mgr_t pointer returned from alsa_ucm_create.
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
* ucm_dev - The ucm device to get capture control for.
* Returns:
* A pointer to the allocated string containing the name of the capture
* control, or NULL if no capture control is found.
*/
-char *ucm_get_cap_control(snd_use_case_mgr_t *mgr, const char *ucm_dev);
+char *ucm_get_cap_control(struct cras_use_case_mgr *mgr, const char *ucm_dev);
/* Gets the mic positions string for internal mic.
* Args:
- * mgr - The snd_use_case_mgr_t pointer returned from alsa_ucm_create.
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
* Returns:
* A pointer to the allocated string containing the mic positions
* information, or NULL if not specified.
*/
-char *ucm_get_mic_positions(snd_use_case_mgr_t *mgr);
+char *ucm_get_mic_positions(struct cras_use_case_mgr *mgr);
/* Gets the new node type name which user wants to override the old one for
* given ucm device.
* Args:
- * mgr - The snd_use_case_mgr_t pointer returned from alsa_ucm_create.
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
* ucm_dev - The ucm device to override node type.
* Returns:
* A pointer to the allocated string containing the new type name,
* or NULL if no override_type_name is found.
*/
-const char *ucm_get_override_type_name(snd_use_case_mgr_t *mgr,
- const char *ucm_dev);
+const char *ucm_get_override_type_name(struct cras_use_case_mgr *mgr,
+ const char *ucm_dev);
/* Gets the name of the ucm device for the given jack name.
* Args:
- * mgr - The snd_use_case_mgr_t pointer returned from alsa_ucm_create.
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
* jack - The name of the jack to search for.
* direction - input or output
* Rreturns:
* A pointer to the allocated string containing the name of the device, or
* NULL if no device is found.
*/
-char *ucm_get_dev_for_jack(snd_use_case_mgr_t *mgr, const char *jack,
+char *ucm_get_dev_for_jack(struct cras_use_case_mgr *mgr, const char *jack,
enum CRAS_STREAM_DIRECTION direction);
/* Gets the name of the ucm device for the given mixer name.
* Args:
- * mgr - The snd_use_case_mgr_t pointer returned from alsa_ucm_create.
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
* mixer - The name of the mixer control to search for.
* dir - input or output
* Rreturns:
* A pointer to the allocated string containing the name of the device, or
* NULL if no device is found.
*/
-char *ucm_get_dev_for_mixer(snd_use_case_mgr_t *mgr, const char *mixer,
+char *ucm_get_dev_for_mixer(struct cras_use_case_mgr *mgr, const char *mixer,
enum CRAS_STREAM_DIRECTION dir);
/* If there is an EDID file variable specified for dev, return it. The EDID
* file will be used for HDMI devices so supported audio formats can be checked.
* Args:
- * mgr - The snd_use_case_mgr_t pointer returned from alsa_ucm_create.
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
* dev - The device to check for an EDID file.
* Returns:
* A string containing the name of the edid file on Success (Must be freed
* later). NULL if none found.
*/
-const char *ucm_get_edid_file_for_dev(snd_use_case_mgr_t *mgr, const char *dev);
+const char *ucm_get_edid_file_for_dev(struct cras_use_case_mgr *mgr,
+ const char *dev);
/* Gets the dsp name which is associated with the given ucm device.
* Args:
- * mgr - The snd_use_case_mgr_t pointer returned from alsa_ucm_create.
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
* ucm_dev - The ucm device to get dsp name for.
* direction - playback(CRAS_STREAM_OUTPUT) or capture(CRAS_STREAM_INPUT).
* Returns:
* A pointer to the allocated string containing the dsp name, or NULL if no
* dsp name is found.
*/
-const char *ucm_get_dsp_name(snd_use_case_mgr_t *mgr, const char *ucm_dev,
- int direction);
+const char *ucm_get_dsp_name(struct cras_use_case_mgr *mgr, const char *ucm_dev,
+ int direction);
/* Gets the default dsp name.
* Args:
- * mgr - The snd_use_case_mgr_t pointer returned from alsa_ucm_create.
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
* direction - playback(CRAS_STREAM_OUTPUT) or capture(CRAS_STREAM_INPUT).
* Returns:
* A pointer to the allocated string containing the default dsp name, or
* NULL if no default dsp name is found.
*/
-const char *ucm_get_dsp_name_default(snd_use_case_mgr_t *mgr, int direction);
+const char *ucm_get_dsp_name_default(struct cras_use_case_mgr *mgr,
+ int direction);
/* Gets the minimum buffer level for an output. This level will add latency to
* all streams playing on the output, but can be used to work around an
* unreliable dma residue.
* Args:
- * mgr - The snd_use_case_mgr_t pointer returned from alsa_ucm_create.
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
*/
-unsigned int ucm_get_min_buffer_level(snd_use_case_mgr_t *mgr);
+unsigned int ucm_get_min_buffer_level(struct cras_use_case_mgr *mgr);
/* Gets the flag for disabling software volume.
* Args:
- * mgr - The snd_use_case_mgr_t pointer returned from alsa_ucm_create.
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
*/
-unsigned int ucm_get_disable_software_volume(snd_use_case_mgr_t *mgr);
+unsigned int ucm_get_disable_software_volume(struct cras_use_case_mgr *mgr);
+
+/* Gets the value for maximum software gain.
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * dev - The device to check for maximum software gain.
+ * gain - The pointer to the returned value;
+ * Returns:
+ * 0 on success, other error codes on failure.
+ */
+int ucm_get_max_software_gain(struct cras_use_case_mgr *mgr, const char *dev,
+ long *gain);
+
+/* Gets the value for default node gain.
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * dev - The device to check for default node gain.
+ * gain - The pointer to the returned value;
+ * Returns:
+ * 0 on success, other error codes on failure.
+ */
+int ucm_get_default_node_gain(struct cras_use_case_mgr *mgr, const char *dev,
+ long *gain);
/* Gets the device name of this device on the card..
*
* Args:
- * mgr - The snd_use_case_mgr_t pointer returned from alsa_ucm_create.
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
* dev - The device to check for device name
* direction - playback(CRAS_STREAM_OUTPUT) or capture(CRAS_STREAM_INPUT).
* Returns:
@@ -189,6 +227,205 @@ unsigned int ucm_get_disable_software_volume(snd_use_case_mgr_t *mgr);
* "card_name:device_index".
*/
const char *ucm_get_device_name_for_dev(
- snd_use_case_mgr_t *mgr, const char *dev,
+ struct cras_use_case_mgr *mgr, const char *dev,
enum CRAS_STREAM_DIRECTION direction);
+
+/* Gets the sample rate at which to run this device.
+ *
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * dev - The device to check for sample rate.
+ * direction - playback(CRAS_STREAM_OUTPUT) or capture(CRAS_STREAM_INPUT).
+ * Returns:
+ * The sample rate if specified, or negative error if not.
+ */
+int ucm_get_sample_rate_for_dev(struct cras_use_case_mgr *mgr, const char *dev,
+ enum CRAS_STREAM_DIRECTION direction);
+
+/* Gets the capture channel map for this device.
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * dev - The device to check for capture channel map.
+ * channel_layout - The channel layout to fill.
+ */
+int ucm_get_capture_chmap_for_dev(struct cras_use_case_mgr *mgr,
+ const char *dev,
+ int8_t *channel_layout);
+
+/* Gets the mixer names for the coupled mixer controls of this device
+ * on the card.
+ *
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * dev - The device to check for coupled mixer controls.
+ * Returns:
+ * A list of cras_alsa_control.
+ */
+struct mixer_name *ucm_get_coupled_mixer_names(
+ struct cras_use_case_mgr *mgr, const char *dev);
+
+/* Gets a list of UCM sections
+ *
+ * The data includes the represented devices and their controls.
+ *
+ * Args:
+ * mgr - The cras_use_case_mgr pointer return from alsa_ucm_create.
+ *
+ * Returns:
+ * A list of ucm_section or NULL. Free it with ucm_section_free_list().
+ */
+struct ucm_section *ucm_get_sections(struct cras_use_case_mgr *mgr);
+
+/* Gets the list of supported hotword model names.
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * Returns:
+ * String containing comma separated model names, e.g 'en,fr,zh'. Needs
+ * to be freed by caller.
+ */
+char *ucm_get_hotword_models(struct cras_use_case_mgr *mgr);
+
+/* Sets the desired hotword model.
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * Returns:
+ * 0 on success or negative error code on failure.
+ */
+int ucm_set_hotword_model(struct cras_use_case_mgr *mgr, const char *model);
+
+/* Checks if this card has fully specified UCM config.
+ *
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * Returns:
+ * 1 if this UCM uses fully specified UCM config. 0 otherwise.
+ */
+int ucm_has_fully_specified_ucm_flag(struct cras_use_case_mgr *mgr);
+
+/* Gets the mixer name of this device on the card.
+ *
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * dev - The device to check for device name
+ * Returns:
+ * A pointer to the allocated string containing the mixer name, or NULL
+ * if no device name is found.
+ */
+const char *ucm_get_mixer_name_for_dev(struct cras_use_case_mgr *mgr,
+ const char *dev);
+
+/* Gets the mixer names for the main volume controls on the card.
+ *
+ * The main volume controls in the list are considered in series.
+ * If 3 controls are specified, MainVolumeNames "A,B,C", with dB ranges
+ * A=-10dB~0dB, B=-20dB~0dB, C=-30dB~0dB, then applying -35dB overall volume
+ * sets A=-10dB, B=-20dB, C=-5dB.
+ * The volume control affects all output on this card, e.g.
+ * speaker and headphone.
+ *
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * Returns:
+ * names - A list of mixer_name.
+ */
+struct mixer_name *ucm_get_main_volume_names(struct cras_use_case_mgr *mgr);
+
+/* The callback to be provided with a reference to the section name.
+ *
+ * Args:
+ * section_name: The name of a SectionDevice in UCM.
+ * arg - Argument to pass to this callback.
+ */
+typedef void (*ucm_list_section_devices_callback)(
+ const char *section_name, void *arg);
+
+/* Invokes the provided callback once for each section with matched device name.
+ *
+ * Iterate through each SectionDevice in UCM of this card. Invoke callback if
+ * "PlaybackPCM" for output or "CapturePCM" for input of the section matches
+ * the specified device_name.
+ *
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * device_name - A string for device name of format "card_name:device_index".
+ * cb - Function to call for each section.
+ * cb_arg - Argument to pass to cb.
+ * Returns:
+ * Number of sections listed.
+ */
+int ucm_list_section_devices_by_device_name(
+ struct cras_use_case_mgr *mgr,
+ enum CRAS_STREAM_DIRECTION direction,
+ const char *device_name,
+ ucm_list_section_devices_callback cb,
+ void *cb_arg);
+
+/* Gets the jack name of this device on the card.
+ *
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * dev - The device to check for jack name.
+ * Returns:
+ * A pointer to the allocated string containing the jack name, or NULL
+ * if no jack name is found.
+ */
+const char *ucm_get_jack_name_for_dev(struct cras_use_case_mgr *mgr,
+ const char *dev);
+
+/* Gets the jack type of this device on the card.
+ *
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * dev - The device to check for jack type.
+ * Returns:
+ * A pointer to the allocated string containing the jack type, or NULL
+ * if no jack type is found or the found jack type is invalid. The valid
+ * jack types are "hctl" or "gpio".
+ */
+const char *ucm_get_jack_type_for_dev(struct cras_use_case_mgr *mgr,
+ const char *dev);
+
+/* Gets the jack switch number for this device.
+ * Some sound cards can detect multiple types of connections into the
+ * audio jack - for example distinguish between line-out and headphones
+ * by measuring the impedance on the other end. In that case we want each
+ * jack to have it's own I/O node so that each can have it's own volume
+ * settings. This allows us to specify the jack used more exactly.
+ * Valid values are defined in /usr/include/linux/input.h.
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * dev - The device to check.
+ * Returns:
+ * A value >= 0 when the switch is defined, or -1 otherwise.
+ */
+int ucm_get_jack_switch_for_dev(struct cras_use_case_mgr *mgr, const char *dev);
+
+/* Gets the DMA period time in microseconds for the given device.
+ *
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * dev - The device to check.
+ * Returns:
+ * A value > 0, or 0 if no period is defined.
+ */
+unsigned int ucm_get_dma_period_for_dev(struct cras_use_case_mgr *mgr,
+ const char *dev);
+
+/* Gets the flag of optimization for no stream state.
+ * This flag enables no_stream ops in alsa_io.
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * Returns:
+ * 1 if the flag is enabled. 0 otherwise.
+ */
+unsigned int ucm_get_optimize_no_stream_flag(struct cras_use_case_mgr *mgr);
+
+/* Retrieve the flag that enables use of htimestamp.
+ * Args:
+ * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create.
+ * Returns:
+ * 1 if the flag is enabled. 0 otherwise.
+ */
+unsigned int ucm_get_enable_htimestamp_flag(struct cras_use_case_mgr *mgr);
+
#endif /* _CRAS_ALSA_UCM_H */
diff --git a/cras/src/server/cras_alsa_ucm_section.c b/cras/src/server/cras_alsa_ucm_section.c
new file mode 100644
index 00000000..179706d6
--- /dev/null
+++ b/cras/src/server/cras_alsa_ucm_section.c
@@ -0,0 +1,129 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "cras_alsa_ucm_section.h"
+#include "cras_alsa_mixer_name.h"
+#include "utlist.h"
+
+static void ucm_section_free(struct ucm_section *section) {
+ if (section->name)
+ free((void *)section->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);
+ mixer_name_free(section->coupled);
+ free(section);
+}
+
+void ucm_section_free_list(struct ucm_section *sections)
+{
+ struct ucm_section *section;
+ DL_FOREACH(sections, section) {
+ DL_DELETE(sections, section);
+ ucm_section_free(section);
+ }
+}
+
+struct ucm_section *ucm_section_create(const char *name,
+ int dev_idx,
+ enum CRAS_STREAM_DIRECTION dir,
+ const char *jack_name,
+ const char *jack_type)
+{
+ struct ucm_section *section_list = NULL;
+ struct ucm_section *section;
+
+ if (!name)
+ return NULL;
+
+ section = (struct ucm_section *)
+ calloc(1, sizeof(struct ucm_section));
+ if (!section)
+ return NULL;
+
+ section->dev_idx = dev_idx;
+ section->dir = dir;
+ section->name = strdup(name);
+ if (!section->name)
+ goto error;
+
+ if (jack_name) {
+ section->jack_name = strdup(jack_name);
+ if (!section->jack_name)
+ goto error;
+ }
+ if (jack_type) {
+ section->jack_type = strdup(jack_type);
+ if (!section->jack_type)
+ goto error;
+ }
+ /* Default to -1 which means auto-detect. */
+ section->jack_switch = -1;
+
+ /* Make sure to initialize this item as a list. */
+ DL_APPEND(section_list, section);
+ return section_list;
+
+error:
+ ucm_section_free(section);
+ return NULL;
+}
+
+int ucm_section_set_mixer_name(struct ucm_section *section,
+ const char *name)
+{
+ if (!section || !name)
+ return -EINVAL;
+
+ if (section->mixer_name)
+ free((void *)section->mixer_name);
+ section->mixer_name = strdup(name);
+ if (!section->mixer_name)
+ return -ENOMEM;
+ return 0;
+}
+
+int ucm_section_add_coupled(struct ucm_section *section,
+ const char *name,
+ mixer_name_type type)
+{
+ struct mixer_name *m_name;
+
+ if (!section || !name || type == MIXER_NAME_UNDEFINED)
+ return -EINVAL;
+
+ m_name = mixer_name_add(NULL, name, section->dir, type);
+ if (!m_name)
+ return -ENOMEM;
+ DL_APPEND(section->coupled, m_name);
+ return 0;
+}
+
+int ucm_section_concat_coupled(struct ucm_section *section,
+ struct mixer_name *coupled)
+{
+ if (!section || !coupled)
+ return -EINVAL;
+ DL_CONCAT(section->coupled, coupled);
+ return 0;
+}
+
+void ucm_section_dump(struct ucm_section *section)
+{
+ syslog(LOG_DEBUG, "section: %s [%d] (%s)",
+ section->name, section->dev_idx,
+ section->dir == CRAS_STREAM_OUTPUT ? "output" : "input");
+ syslog(LOG_DEBUG, " jack: %s %s",
+ section->jack_name, section->jack_type);
+ syslog(LOG_DEBUG, " mixer_name: %s", section->mixer_name);
+ mixer_name_dump(section->coupled, " coupled");
+}
diff --git a/cras/src/server/cras_alsa_ucm_section.h b/cras/src/server/cras_alsa_ucm_section.h
new file mode 100644
index 00000000..bd0c2ef5
--- /dev/null
+++ b/cras/src/server/cras_alsa_ucm_section.h
@@ -0,0 +1,105 @@
+/* Copyright 2016 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_ALSA_UCM_SECTION_H
+#define _CRAS_ALSA_UCM_SECTION_H
+
+#include "cras_types.h"
+#include "cras_alsa_mixer_name.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Represents an ALSA UCM section. */
+struct ucm_section {
+ const char *name; /* Section name. */
+ int dev_idx; /* Device PCM index. */
+ enum CRAS_STREAM_DIRECTION dir; /* Output or Input. */
+ const char *jack_name; /* Associated jack's name. */
+ const char *jack_type; /* Associated jack's type. */
+ int jack_switch; /* Switch number for jack from
+ * linux/input.h, or -1. */
+ const char *mixer_name; /* MixerName value. */
+ struct mixer_name *coupled; /* CoupledMixers value. */
+ struct ucm_section *prev, *next;
+};
+
+/* Create a single UCM section.
+ *
+ * Args:
+ * name - Section name (must not be NULL).
+ * dev_idx - Section's device index (PCM number).
+ * dir - Device direction: INPUT or OUTPUT.
+ * jack_name - Name of an associated jack (or NULL).
+ * jack_type - Type of the associated jack (or NULL).
+ *
+ * Returns:
+ * A valid pointer on success, NULL for memory allocation error.
+ */
+struct ucm_section *ucm_section_create(const char *name,
+ int dev_idx,
+ enum CRAS_STREAM_DIRECTION dir,
+ const char *jack_name,
+ const char *jack_type);
+
+/* Sets the mixer_name value for the given section.
+ *
+ * Args:
+ * section - Section to manipulate.
+ * name - The name of the control.
+ *
+ * Returns:
+ * 0 for success, -EINVAL for invalid arguments, or -ENOMEM.
+ */
+int ucm_section_set_mixer_name(struct ucm_section *section,
+ const char *name);
+
+/* Add a single coupled control to this section.
+ * Control has the same direction as the section.
+ *
+ * Args:
+ * section - Section to manipulate.
+ * name - Coupled control name to add.
+ * type - The type of control.
+ *
+ * Returns:
+ * 0 for success, -EINVAL for invalid arguments, or -ENOMEM.
+ */
+int ucm_section_add_coupled(struct ucm_section *section,
+ const char *name,
+ mixer_name_type type);
+
+/* Concatenate a list of coupled controls to this section.
+ *
+ * Args:
+ * section - Section to manipulate.
+ * coupled - Coupled control names to add.
+ *
+ * Returns:
+ * 0 for success, -EINVAL for invalid arguments (NULL args).
+ */
+int ucm_section_concat_coupled(struct ucm_section *section,
+ struct mixer_name *coupled);
+
+/* Frees a list of sections.
+ *
+ * Args:
+ * sections - List of sections to free.
+ */
+void ucm_section_free_list(struct ucm_section *sections);
+
+/* Dump details on this section to syslog(LOG_DEBUG).
+ *
+ * Args:
+ * section - Section to dump.
+ */
+void ucm_section_dump(struct ucm_section *section);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CRAS_ALSA_MIXER_NAME_H */
diff --git a/cras/src/server/cras_audio_area.c b/cras/src/server/cras_audio_area.c
index 3e30a021..5def7eaf 100644
--- a/cras/src/server/cras_audio_area.c
+++ b/cras/src/server/cras_audio_area.c
@@ -27,22 +27,14 @@ unsigned int cras_audio_area_copy(const struct cras_audio_area *dst,
const struct cras_audio_format *dst_fmt,
const struct cras_audio_area *src,
unsigned int src_offset,
- unsigned int skip_zero)
+ float software_gain_scaler)
{
unsigned int src_idx, dst_idx;
unsigned int ncopy;
- unsigned int dst_format_bytes = cras_get_format_bytes(dst_fmt);
uint8_t *schan, *dchan;
ncopy = MIN(src->frames - src_offset, dst->frames - dst_offset);
- /* TODO(dgreid) - make it so this isn't needed, can copy first stream of
- * each channel. */
- if (!skip_zero)
- memset(dst->channels[0].buf +
- dst_offset * dst->channels[0].step_bytes, 0,
- ncopy * dst_format_bytes);
-
/* TODO(dgreid) - this replaces a memcpy, it needs to be way faster. */
for (src_idx = 0; src_idx < src->num_channels; src_idx++) {
@@ -56,10 +48,11 @@ unsigned int cras_audio_area_copy(const struct cras_audio_area *dst,
dchan = dst->channels[dst_idx].buf +
dst_offset * dst->channels[dst_idx].step_bytes;
- cras_mix_add_stride(dst_fmt->format, dchan, schan,
+ cras_mix_add_scale_stride(dst_fmt->format, dchan, schan,
ncopy,
dst->channels[dst_idx].step_bytes,
- src->channels[src_idx].step_bytes);
+ src->channels[src_idx].step_bytes,
+ software_gain_scaler);
}
}
diff --git a/cras/src/server/cras_audio_area.h b/cras/src/server/cras_audio_area.h
index 8771a5e8..1111ee41 100644
--- a/cras/src/server/cras_audio_area.h
+++ b/cras/src/server/cras_audio_area.h
@@ -64,7 +64,7 @@ struct cras_audio_area *cras_audio_area_create(int num_channels);
* format - The format of dst area.
* src - The source audio area.
* src_offset - The offset of src audio area in frames.
- * skip_zero - Skip zeroing the area before copying the data.
+ * software_gain_scaler - The software gain scaler needed.
* Returns the number of frames copied.
*/
unsigned int cras_audio_area_copy(const struct cras_audio_area *dst,
@@ -72,7 +72,7 @@ unsigned int cras_audio_area_copy(const struct cras_audio_area *dst,
const struct cras_audio_format *dst_fmt,
const struct cras_audio_area *src,
unsigned int src_offset,
- unsigned int skip_zero);
+ float software_gain_scaler);
/*
* Destroys a cras_audio_area.
diff --git a/cras/src/server/cras_bt_adapter.c b/cras/src/server/cras_bt_adapter.c
index d050f2d0..f84e01e8 100644
--- a/cras/src/server/cras_bt_adapter.c
+++ b/cras/src/server/cras_bt_adapter.c
@@ -50,6 +50,7 @@ static int cras_bt_adapter_query_bus_type(struct cras_bt_adapter *adapter)
}
/* dev_id = 0 for hci0 */
+ dev_info.type = 0;
dev_info.dev_id = atoi(pos + 3);
err = ioctl(ctl, HCIGETDEVINFO, (void *)&dev_info);
if (err) {
@@ -111,6 +112,9 @@ struct cras_bt_adapter *cras_bt_adapter_get(const char *object_path)
{
struct cras_bt_adapter *adapter;
+ if (object_path == NULL)
+ return NULL;
+
DL_FOREACH(adapters, adapter) {
if (strcmp(adapter->object_path, object_path) == 0)
return adapter;
diff --git a/cras/src/server/cras_bt_constants.h b/cras/src/server/cras_bt_constants.h
index 23ec8dde..13a1737a 100644
--- a/cras/src/server/cras_bt_constants.h
+++ b/cras/src/server/cras_bt_constants.h
@@ -13,9 +13,9 @@
#define BLUEZ_INTERFACE_MEDIA "org.bluez.Media1"
#define BLUEZ_INTERFACE_MEDIA_ENDPOINT "org.bluez.MediaEndpoint1"
#define BLUEZ_INTERFACE_MEDIA_TRANSPORT "org.bluez.MediaTransport1"
+#define BLUEZ_INTERFACE_PLAYER "org.bluez.MediaPlayer1"
#define BLUEZ_INTERFACE_PROFILE "org.bluez.Profile1"
#define BLUEZ_PROFILE_MGMT_INTERFACE "org.bluez.ProfileManager1"
-
/* Remove once our D-Bus header files are updated to define this. */
#ifndef DBUS_INTERFACE_OBJECT_MANAGER
#define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager"
diff --git a/cras/src/server/cras_bt_device.c b/cras/src/server/cras_bt_device.c
index 195eddc5..682edba4 100644
--- a/cras/src/server/cras_bt_device.c
+++ b/cras/src/server/cras_bt_device.c
@@ -18,6 +18,7 @@
#include <syslog.h>
#include "bluetooth.h"
+#include "cras_a2dp_endpoint.h"
#include "cras_bt_adapter.h"
#include "cras_bt_device.h"
#include "cras_bt_constants.h"
@@ -35,46 +36,69 @@
#define DEFAULT_HFP_MTU_BYTES 48
+static const unsigned int PROFILE_SWITCH_DELAY_MS = 500;
+
+/* Check profile connections every 2 seconds and rerty 30 times maximum.
+ * Attemp to connect profiles which haven't been ready every 3 retries.
+ */
+static const unsigned int CONN_WATCH_PERIOD_MS = 2000;
+static const unsigned int CONN_WATCH_MAX_RETRIES = 30;
+static const unsigned int PROFILE_CONN_RETRIES = 3;
+
/* Object to represent a general bluetooth device, and used to
* associate with some CRAS modules if it supports audio.
* Members:
+ * conn - The dbus connection object used to send message to bluetoothd.
* object_path - Object path of the bluetooth device.
- * adapter - The adapter object associates with this device.
+ * adapter - The object path of the adapter associates with this device.
* address - The BT address of this device.
* name - The readable name of this device.
* bluetooth_class - The bluetooth class of this device.
* paired - If this device is paired.
* trusted - If this device is trusted.
* connected - If this devices is connected.
+ * connected_profiles - OR'ed all connected audio profiles.
* profiles - OR'ed by all audio profiles this device supports.
* bt_iodevs - The pointer to the cras_iodevs of this device.
* active_profile - The flag to indicate the active audio profile this
* device is currently using.
- * a2dp_delay_timer - The timer used to delay the allocation of HFP/HSP
- * stuff until a2dp connection is established.
+ * conn_watch_retries - The retry count for conn_watch_timer.
+ * conn_watch_timer - The timer used to watch connected profiles and start
+ * BT audio input/ouput when all profiles are ready.
+ * suspend_timer - The timer used to suspend device.
+ * switch_profile_timer - The timer used to delay enabling iodev after
+ * profile switch.
* append_iodev_cb - The callback to trigger when an iodev is appended.
*/
struct cras_bt_device {
+ DBusConnection *conn;
char *object_path;
- struct cras_bt_adapter *adapter;
+ char *adapter_obj_path;
char *address;
char *name;
uint32_t bluetooth_class;
int paired;
int trusted;
int connected;
+ enum cras_bt_device_profile connected_profiles;
enum cras_bt_device_profile profiles;
struct cras_iodev *bt_iodevs[CRAS_NUM_DIRECTIONS];
unsigned int active_profile;
- struct cras_timer *a2dp_delay_timer;
+ int use_hardware_volume;
+ int conn_watch_retries;
+ struct cras_timer *conn_watch_timer;
+ struct cras_timer *suspend_timer;
+ struct cras_timer *switch_profile_timer;
void (*append_iodev_cb)(void *data);
struct cras_bt_device *prev, *next;
};
enum BT_DEVICE_COMMAND {
- BT_DEVICE_SWITCH_PROFILE_ON_CLOSE,
- BT_DEVICE_SWITCH_PROFILE_ON_OPEN,
+ BT_DEVICE_CANCEL_SUSPEND,
+ BT_DEVICE_SCHEDULE_SUSPEND,
+ BT_DEVICE_SWITCH_PROFILE,
+ BT_DEVICE_SWITCH_PROFILE_ENABLE_DEV,
};
struct bt_device_msg {
@@ -82,6 +106,7 @@ struct bt_device_msg {
enum BT_DEVICE_COMMAND cmd;
struct cras_bt_device *device;
struct cras_iodev *dev;
+ unsigned int arg;
};
static struct cras_bt_device *devices;
@@ -114,7 +139,8 @@ enum cras_bt_device_profile cras_bt_device_profile_from_uuid(const char *uuid)
return 0;
}
-struct cras_bt_device *cras_bt_device_create(const char *object_path)
+struct cras_bt_device *cras_bt_device_create(DBusConnection *conn,
+ const char *object_path)
{
struct cras_bt_device *device;
@@ -122,6 +148,7 @@ struct cras_bt_device *cras_bt_device_create(const char *object_path)
if (device == NULL)
return NULL;
+ device->conn = conn;
device->object_path = strdup(object_path);
if (device->object_path == NULL) {
free(device);
@@ -133,6 +160,20 @@ struct cras_bt_device *cras_bt_device_create(const char *object_path)
return device;
}
+static void on_connect_profile_reply(DBusPendingCall *pending_call, void *data)
+{
+ DBusMessage *reply;
+
+ reply = dbus_pending_call_steal_reply(pending_call);
+ dbus_pending_call_unref(pending_call);
+
+ if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+ syslog(LOG_ERR, "Connect profile message replied error: %s",
+ dbus_message_get_error_name(reply));
+
+ dbus_message_unref(reply);
+}
+
static void on_disconnect_reply(DBusPendingCall *pending_call, void *data)
{
DBusMessage *reply;
@@ -146,6 +187,51 @@ static void on_disconnect_reply(DBusPendingCall *pending_call, void *data)
dbus_message_unref(reply);
}
+int cras_bt_device_connect_profile(DBusConnection *conn,
+ struct cras_bt_device *device,
+ const char *uuid)
+{
+ DBusMessage *method_call;
+ DBusError dbus_error;
+ DBusPendingCall *pending_call;
+
+ method_call = dbus_message_new_method_call(
+ BLUEZ_SERVICE,
+ device->object_path,
+ BLUEZ_INTERFACE_DEVICE,
+ "ConnectProfile");
+ if (!method_call)
+ return -ENOMEM;
+
+ if (!dbus_message_append_args(method_call,
+ DBUS_TYPE_STRING,
+ &uuid,
+ DBUS_TYPE_INVALID))
+ return -ENOMEM;
+
+ dbus_error_init(&dbus_error);
+
+ pending_call = NULL;
+ if (!dbus_connection_send_with_reply(conn,
+ method_call,
+ &pending_call,
+ DBUS_TIMEOUT_USE_DEFAULT)) {
+ dbus_message_unref(method_call);
+ syslog(LOG_ERR, "Failed to send Disconnect message");
+ return -EIO;
+ }
+
+ dbus_message_unref(method_call);
+ if (!dbus_pending_call_set_notify(pending_call,
+ on_connect_profile_reply,
+ conn, NULL)) {
+ dbus_pending_call_cancel(pending_call);
+ dbus_pending_call_unref(pending_call);
+ return -EIO;
+ }
+ return 0;
+}
+
int cras_bt_device_disconnect(DBusConnection *conn,
struct cras_bt_device *device)
{
@@ -186,8 +272,15 @@ int cras_bt_device_disconnect(DBusConnection *conn,
void cras_bt_device_destroy(struct cras_bt_device *device)
{
+ struct cras_tm *tm = cras_system_state_get_tm();
DL_DELETE(devices, device);
+ if (device->conn_watch_timer)
+ cras_tm_cancel_timer(tm, device->conn_watch_timer);
+ if (device->switch_profile_timer)
+ cras_tm_cancel_timer(tm, device->switch_profile_timer);
+ if (device->suspend_timer)
+ cras_tm_cancel_timer(tm, device->suspend_timer);
free(device->object_path);
free(device->address);
free(device->name);
@@ -248,7 +341,7 @@ const char *cras_bt_device_object_path(const struct cras_bt_device *device)
struct cras_bt_adapter *cras_bt_device_adapter(
const struct cras_bt_device *device)
{
- return device->adapter;
+ return cras_bt_adapter_get(device->adapter_obj_path);
}
const char *cras_bt_device_address(const struct cras_bt_device *device)
@@ -304,7 +397,7 @@ void cras_bt_device_append_iodev(struct cras_bt_device *device,
static void bt_device_switch_profile(struct cras_bt_device *device,
struct cras_iodev *bt_iodev,
- int on_open);
+ int enable_dev);
void cras_bt_device_rm_iodev(struct cras_bt_device *device,
struct cras_iodev *iodev)
@@ -347,6 +440,11 @@ destroy_bt_io:
cras_bt_device_set_active_profile(device, 0);
}
+void cras_bt_device_a2dp_configured(struct cras_bt_device *device)
+{
+ device->connected_profiles |= CRAS_BT_DEVICE_PROFILE_A2DP_SINK;
+}
+
int cras_bt_device_has_a2dp(struct cras_bt_device *device)
{
struct cras_iodev *odev = device->bt_iodevs[CRAS_STREAM_OUTPUT];
@@ -361,27 +459,43 @@ int cras_bt_device_can_switch_to_a2dp(struct cras_bt_device *device)
struct cras_iodev *idev = device->bt_iodevs[CRAS_STREAM_INPUT];
return cras_bt_device_has_a2dp(device) &&
- (!idev || !idev->is_open(idev));
+ (!idev || !cras_iodev_is_open(idev));
}
-void cras_bt_device_add_a2dp_delay_timer(struct cras_bt_device *device,
- struct cras_timer *timer)
+int cras_bt_device_audio_gateway_initialized(struct cras_bt_device *device)
{
- device->a2dp_delay_timer = timer;
-}
+ int rc = 0;
+ struct cras_tm *tm;
-void cras_bt_device_cancel_a2dp_delay_timer(struct cras_bt_device *device)
-{
- struct cras_tm *tm = cras_system_state_get_tm();
+ /* Marks HFP/HSP as connected. This is what connection watcher
+ * checks. */
+ device->connected_profiles |=
+ (CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE |
+ CRAS_BT_DEVICE_PROFILE_HSP_HEADSET);
- if (device->a2dp_delay_timer)
- cras_tm_cancel_timer(tm, device->a2dp_delay_timer);
- device->a2dp_delay_timer = NULL;
-}
+ /* If this is a HFP/HSP only headset, no need to wait for A2DP. */
+ if (!cras_bt_device_supports_profile(
+ device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK)) {
-void cras_bt_device_rm_a2dp_delay_timer(struct cras_bt_device *device)
-{
- device->a2dp_delay_timer = NULL;
+ syslog(LOG_DEBUG,
+ "Start HFP audio gateway as A2DP is not supported");
+
+ rc = cras_hfp_ag_start(device);
+ if (rc) {
+ syslog(LOG_ERR, "Start audio gateway failed");
+ return rc;
+ }
+ if (device->conn_watch_timer) {
+ tm = cras_system_state_get_tm();
+ cras_tm_cancel_timer(tm, device->conn_watch_timer);
+ device->conn_watch_timer = NULL;
+ }
+ } else {
+ syslog(LOG_DEBUG, "HFP audio gateway is connected but A2DP "
+ "is not connected yet");
+ }
+
+ return rc;
}
int cras_bt_device_get_active_profile(const struct cras_bt_device *device)
@@ -434,10 +548,120 @@ static void cras_bt_device_log_profile(const struct cras_bt_device *device,
}
}
+static int cras_bt_device_is_profile_connected(
+ const struct cras_bt_device *device,
+ enum cras_bt_device_profile profile)
+{
+ return !!(device->connected_profiles & profile);
+}
+
+static void bt_device_schedule_suspend(struct cras_bt_device *device,
+ unsigned int msec);
+
+/* Callback used to periodically check if supported profiles are connected. */
+static void bt_device_conn_watch_cb(struct cras_timer *timer, void *arg)
+{
+ struct cras_tm *tm;
+ struct cras_bt_device *device = (struct cras_bt_device *)arg;
+
+ device->conn_watch_timer = NULL;
+
+ /* If A2DP is not ready, try connect it after a while. */
+ if (cras_bt_device_supports_profile(
+ device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK) &&
+ !cras_bt_device_is_profile_connected(
+ device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK)) {
+ if (0 == device->conn_watch_retries % PROFILE_CONN_RETRIES)
+ cras_bt_device_connect_profile(
+ device->conn, device, A2DP_SINK_UUID);
+ goto arm_retry_timer;
+ }
+
+ /* If HFP is not ready, try connect it after a while. */
+ if (cras_bt_device_supports_profile(
+ device, CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE) &&
+ !cras_bt_device_is_profile_connected(
+ device, CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE)) {
+ if (0 == device->conn_watch_retries % PROFILE_CONN_RETRIES)
+ cras_bt_device_connect_profile(
+ device->conn, device, HFP_HF_UUID);
+ goto arm_retry_timer;
+ }
+
+ if (cras_bt_device_is_profile_connected(
+ device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK)) {
+ /* When A2DP-only device connected, suspend all HFP/HSP audio
+ * gateways. */
+ if (!cras_bt_device_supports_profile(device,
+ CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE |
+ CRAS_BT_DEVICE_PROFILE_HSP_HEADSET))
+ cras_hfp_ag_suspend();
+
+ cras_a2dp_start(device);
+ }
+
+ if (cras_bt_device_is_profile_connected(
+ device, CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE))
+ cras_hfp_ag_start(device);
+ return;
+
+arm_retry_timer:
+
+ syslog(LOG_DEBUG, "conn_watch_retries: %d", device->conn_watch_retries);
+
+ if (--device->conn_watch_retries) {
+ tm = cras_system_state_get_tm();
+ device->conn_watch_timer = cras_tm_create_timer(tm,
+ CONN_WATCH_PERIOD_MS,
+ bt_device_conn_watch_cb, device);
+ } else {
+ syslog(LOG_ERR, "Connection watch timeout.");
+ bt_device_schedule_suspend(device, 0);
+ }
+}
+
+static void cras_bt_device_start_new_conn_watch_timer(
+ struct cras_bt_device *device)
+{
+ struct cras_tm *tm = cras_system_state_get_tm();
+
+ if (device->conn_watch_timer) {
+ cras_tm_cancel_timer(tm, device->conn_watch_timer);
+ }
+ device->conn_watch_retries = CONN_WATCH_MAX_RETRIES;
+ device->conn_watch_timer = cras_tm_create_timer(tm,
+ CONN_WATCH_PERIOD_MS,
+ bt_device_conn_watch_cb, device);
+}
+
+static void cras_bt_device_set_connected(struct cras_bt_device *device,
+ int value)
+{
+ struct cras_tm *tm = cras_system_state_get_tm();
+
+ if (device->connected && !value) {
+ cras_bt_profile_on_device_disconnected(device);
+ /* Device is disconnected, resets connected profiles. */
+ device->connected_profiles = 0;
+ }
+
+ device->connected = value;
+
+ if (device->connected) {
+ cras_bt_device_start_new_conn_watch_timer(device);
+ } else if (device->conn_watch_timer) {
+ cras_tm_cancel_timer(tm, device->conn_watch_timer);
+ device->conn_watch_timer = NULL;
+ }
+}
+
void cras_bt_device_update_properties(struct cras_bt_device *device,
DBusMessageIter *properties_array_iter,
DBusMessageIter *invalidated_array_iter)
{
+
+ int get_profile = 0;
+
while (dbus_message_iter_get_arg_type(properties_array_iter) !=
DBUS_TYPE_INVALID) {
DBusMessageIter properties_dict_iter, variant_iter;
@@ -459,16 +683,14 @@ void cras_bt_device_update_properties(struct cras_bt_device *device,
dbus_message_iter_get_basic(&variant_iter, &value);
if (strcmp(key, "Adapter") == 0) {
- device->adapter = cras_bt_adapter_get(value);
-
+ free(device->adapter_obj_path);
+ device->adapter_obj_path = strdup(value);
} else if (strcmp(key, "Address") == 0) {
free(device->address);
device->address = strdup(value);
-
} else if (strcmp(key, "Alias") == 0) {
free(device->name);
device->name = strdup(value);
-
}
} else if (type == DBUS_TYPE_UINT32) {
@@ -489,10 +711,7 @@ void cras_bt_device_update_properties(struct cras_bt_device *device,
} else if (strcmp(key, "Trusted") == 0) {
device->trusted = value;
} else if (strcmp(key, "Connected") == 0) {
- if (device->connected && !value)
- cras_bt_profile_on_device_disconnected(
- device);
- device->connected = value;
+ cras_bt_device_set_connected(device, value);
}
} else if (strcmp(
@@ -508,6 +727,8 @@ void cras_bt_device_update_properties(struct cras_bt_device *device,
const char *uuid;
enum cras_bt_device_profile profile;
+ get_profile = 1;
+
dbus_message_iter_get_basic(&uuid_array_iter,
&uuid);
profile = cras_bt_device_profile_from_uuid(
@@ -531,7 +752,8 @@ void cras_bt_device_update_properties(struct cras_bt_device *device,
dbus_message_iter_get_basic(invalidated_array_iter, &key);
if (strcmp(key, "Adapter") == 0) {
- device->adapter = NULL;
+ free(device->adapter_obj_path);
+ device->adapter_obj_path = NULL;
} else if (strcmp(key, "Address") == 0) {
free(device->address);
device->address = NULL;
@@ -552,6 +774,15 @@ void cras_bt_device_update_properties(struct cras_bt_device *device,
dbus_message_iter_next(invalidated_array_iter);
}
+
+ /* If updated properties includes profile, and device is connected,
+ * we need to start connection watcher. This is needed because on
+ * some bluetooth device, supported profiles do not present when
+ * device interface is added and they are updated later.
+ */
+ if (get_profile && device->connected) {
+ cras_bt_device_start_new_conn_watch_timer(device);
+ }
}
/* Converts bluetooth address string into sockaddr structure. The address
@@ -632,6 +863,13 @@ int cras_bt_device_sco_connect(struct cras_bt_device *device)
goto error;
}
+ if (pollfds[0].revents & (POLLERR | POLLHUP)) {
+ syslog(LOG_ERR, "SCO socket error, revents: %u",
+ pollfds[0].revents);
+ bt_device_schedule_suspend(device, 0);
+ goto error;
+ }
+
return sk;
error:
@@ -644,8 +882,10 @@ int cras_bt_device_sco_mtu(struct cras_bt_device *device, int sco_socket)
{
struct sco_options so;
socklen_t len = sizeof(so);
+ struct cras_bt_adapter *adapter;
- if (cras_bt_adapter_on_usb(device->adapter))
+ adapter = cras_bt_adapter_get(device->adapter_obj_path);
+ if (cras_bt_adapter_on_usb(adapter))
return DEFAULT_HFP_MTU_BYTES;
if (getsockopt(sco_socket, SOL_SCO, SCO_OPTIONS, &so, &len) < 0) {
@@ -655,15 +895,57 @@ int cras_bt_device_sco_mtu(struct cras_bt_device *device, int sco_socket)
return so.mtu;
}
-int cras_bt_device_set_speaker_gain(struct cras_bt_device *device, int gain)
+void cras_bt_device_set_use_hardware_volume(struct cras_bt_device *device,
+ int use_hardware_volume)
+{
+ struct cras_iodev *iodev;
+
+ device->use_hardware_volume = use_hardware_volume;
+ iodev = device->bt_iodevs[CRAS_STREAM_OUTPUT];
+ if (iodev)
+ iodev->software_volume_needed = !use_hardware_volume;
+}
+
+int cras_bt_device_get_use_hardware_volume(struct cras_bt_device *device)
+{
+ return device->use_hardware_volume;
+}
+
+static void init_bt_device_msg(struct bt_device_msg *msg,
+ enum BT_DEVICE_COMMAND cmd,
+ struct cras_bt_device *device,
+ struct cras_iodev *dev,
+ unsigned int arg)
{
- struct hfp_slc_handle *slc_handle;
+ memset(msg, 0, sizeof(*msg));
+ msg->header.type = CRAS_MAIN_BT;
+ msg->header.length = sizeof(*msg);
+ msg->cmd = cmd;
+ msg->device = device;
+ msg->dev = dev;
+ msg->arg = arg;
+}
- slc_handle = cras_hfp_ag_get_slc(device);
- if (!slc_handle)
- return -EINVAL;
+int cras_bt_device_cancel_suspend(struct cras_bt_device *device)
+{
+ struct bt_device_msg msg;
+ int rc;
- return hfp_event_speaker_gain(slc_handle, gain);
+ init_bt_device_msg(&msg, BT_DEVICE_CANCEL_SUSPEND, device, NULL, 0);
+ rc = cras_main_message_send((struct cras_main_message *)&msg);
+ return rc;
+}
+
+int cras_bt_device_schedule_suspend(struct cras_bt_device *device,
+ unsigned int msec)
+{
+ struct bt_device_msg msg;
+ int rc;
+
+ init_bt_device_msg(&msg, BT_DEVICE_SCHEDULE_SUSPEND, device,
+ NULL, msec);
+ rc = cras_main_message_send((struct cras_main_message *)&msg);
+ return rc;
}
/* This diagram describes how the profile switching happens. When
@@ -688,33 +970,26 @@ int cras_bt_device_set_speaker_gain(struct cras_bt_device *device, int gain)
* | bt device +------------+ |
* +--------------------------------------------------------------+
*/
-int cras_bt_device_switch_profile_on_open(struct cras_bt_device *device,
- struct cras_iodev *bt_iodev)
+int cras_bt_device_switch_profile_enable_dev(struct cras_bt_device *device,
+ struct cras_iodev *bt_iodev)
{
struct bt_device_msg msg;
int rc;
- msg.header.type = CRAS_MAIN_BT;
- msg.header.length = sizeof(msg);
- msg.cmd = BT_DEVICE_SWITCH_PROFILE_ON_OPEN;
- msg.device = device;
- msg.dev = bt_iodev;
-
+ init_bt_device_msg(&msg, BT_DEVICE_SWITCH_PROFILE_ENABLE_DEV,
+ device, bt_iodev, 0);
rc = cras_main_message_send((struct cras_main_message *)&msg);
return rc;
}
-int cras_bt_device_switch_profile_on_close(struct cras_bt_device *device,
- struct cras_iodev *bt_iodev)
+int cras_bt_device_switch_profile(struct cras_bt_device *device,
+ struct cras_iodev *bt_iodev)
{
struct bt_device_msg msg;
int rc;
- msg.header.type = CRAS_MAIN_BT;
- msg.header.length = sizeof(msg);
- msg.cmd = BT_DEVICE_SWITCH_PROFILE_ON_CLOSE;
- msg.device = device;
- msg.dev = bt_iodev;
+ init_bt_device_msg(&msg, BT_DEVICE_SWITCH_PROFILE,
+ device, bt_iodev, 0);
rc = cras_main_message_send((struct cras_main_message *)&msg);
return rc;
}
@@ -724,19 +999,45 @@ void cras_bt_device_iodev_buffer_size_changed(struct cras_bt_device *device)
struct cras_iodev *iodev;
iodev = device->bt_iodevs[CRAS_STREAM_INPUT];
- if (iodev && iodev->is_open(iodev))
+ if (iodev && cras_iodev_is_open(iodev))
cras_bt_io_update_buffer_size(iodev);
iodev = device->bt_iodevs[CRAS_STREAM_OUTPUT];
- if (iodev && iodev->is_open(iodev))
+ if (iodev && cras_iodev_is_open(iodev))
cras_bt_io_update_buffer_size(iodev);
}
+static void profile_switch_delay_cb(struct cras_timer *timer, void *arg)
+{
+ struct cras_bt_device *device = (struct cras_bt_device *)arg;
+ struct cras_iodev *iodev;
+
+ device->switch_profile_timer = NULL;
+ iodev = device->bt_iodevs[CRAS_STREAM_OUTPUT];
+ if (!iodev)
+ return;
+ iodev->update_active_node(iodev, 0, 1);
+ cras_iodev_list_enable_dev(iodev);
+}
+
+static void bt_device_switch_profile_with_delay(struct cras_bt_device *device,
+ unsigned int delay_ms)
+{
+ struct cras_tm *tm = cras_system_state_get_tm();
+
+ if (device->switch_profile_timer) {
+ cras_tm_cancel_timer(tm, device->switch_profile_timer);
+ device->switch_profile_timer = NULL;
+ }
+ device->switch_profile_timer = cras_tm_create_timer(
+ tm, delay_ms, profile_switch_delay_cb, device);
+}
+
/* Switches associated bt iodevs to use the active profile. This is
* achieved by close the iodevs, update their active nodes, and then
* finally reopen them. */
static void bt_device_switch_profile(struct cras_bt_device *device,
struct cras_iodev *bt_iodev,
- int on_open)
+ int enable_dev)
{
struct cras_iodev *iodev;
int was_enabled[CRAS_NUM_DIRECTIONS] = {0};
@@ -759,28 +1060,76 @@ static void bt_device_switch_profile(struct cras_bt_device *device,
iodev = device->bt_iodevs[dir];
if (!iodev)
continue;
+
/* If the iodev was active or this profile switching is
* triggered at opening iodev, add it to active dev list.
+ * However for the output iodev, adding it back to active dev
+ * list could cause immediate switching from HFP to A2DP if
+ * there exists an output stream. Certain headset/speaker
+ * would fail to playback afterwards when the switching happens
+ * too soon, so put this task in a delayed callback.
*/
if (was_enabled[dir] ||
- (on_open && iodev == bt_iodev)) {
- iodev->update_active_node(iodev, 0);
- cras_iodev_list_enable_dev(iodev);
+ (enable_dev && iodev == bt_iodev)) {
+ if (dir == CRAS_STREAM_INPUT) {
+ iodev->update_active_node(iodev, 0, 1);
+ cras_iodev_list_enable_dev(iodev);
+ } else {
+ bt_device_switch_profile_with_delay(
+ device,
+ PROFILE_SWITCH_DELAY_MS);
+ }
}
}
}
+static void bt_device_suspend_cb(struct cras_timer *timer, void *arg)
+{
+ struct cras_bt_device *device = (struct cras_bt_device *)arg;
+
+ device->suspend_timer = NULL;
+
+ cras_a2dp_suspend_connected_device(device);
+ cras_hfp_ag_suspend_connected_device(device);
+}
+
+static void bt_device_schedule_suspend(struct cras_bt_device *device,
+ unsigned int msec)
+{
+ struct cras_tm *tm = cras_system_state_get_tm();
+
+ if (device->suspend_timer)
+ return;
+ device->suspend_timer = cras_tm_create_timer(tm, msec,
+ bt_device_suspend_cb, device);
+}
+
+static void bt_device_cancel_suspend(struct cras_bt_device *device)
+{
+ struct cras_tm *tm = cras_system_state_get_tm();
+ if (device->suspend_timer == NULL)
+ return;
+ cras_tm_cancel_timer(tm, device->suspend_timer);
+ device->suspend_timer = NULL;
+}
+
static void bt_device_process_msg(struct cras_main_message *msg, void *arg)
{
struct bt_device_msg *bt_msg = (struct bt_device_msg *)msg;
switch (bt_msg->cmd) {
- case BT_DEVICE_SWITCH_PROFILE_ON_CLOSE:
+ case BT_DEVICE_SWITCH_PROFILE:
bt_device_switch_profile(bt_msg->device, bt_msg->dev, 0);
break;
- case BT_DEVICE_SWITCH_PROFILE_ON_OPEN:
+ case BT_DEVICE_SWITCH_PROFILE_ENABLE_DEV:
bt_device_switch_profile(bt_msg->device, bt_msg->dev, 1);
break;
+ case BT_DEVICE_SCHEDULE_SUSPEND:
+ bt_device_schedule_suspend(bt_msg->device, bt_msg->arg);
+ break;
+ case BT_DEVICE_CANCEL_SUSPEND:
+ bt_device_cancel_suspend(bt_msg->device);
+ break;
default:
break;
}
@@ -791,3 +1140,22 @@ void cras_bt_device_start_monitor()
cras_main_message_add_handler(CRAS_MAIN_BT,
bt_device_process_msg, NULL);
}
+
+void cras_bt_device_update_hardware_volume(struct cras_bt_device *device,
+ int volume)
+{
+ struct cras_iodev *iodev;
+
+ iodev = device->bt_iodevs[CRAS_STREAM_OUTPUT];
+ if (iodev == NULL)
+ return;
+
+ /* Check if this BT device is okay to use hardware volume. If not
+ * then ignore the reported volume change event.
+ */
+ if (!cras_bt_device_get_use_hardware_volume(device))
+ return;
+
+ iodev->active_node->volume = volume;
+ cras_iodev_list_notify_node_volume(iodev->active_node);
+}
diff --git a/cras/src/server/cras_bt_device.h b/cras/src/server/cras_bt_device.h
index 2bc0fe19..50468ab8 100644
--- a/cras/src/server/cras_bt_device.h
+++ b/cras/src/server/cras_bt_device.h
@@ -26,7 +26,8 @@ enum cras_bt_device_profile {
enum cras_bt_device_profile cras_bt_device_profile_from_uuid(const char *uuid);
-struct cras_bt_device *cras_bt_device_create(const char *object_path);
+struct cras_bt_device *cras_bt_device_create(DBusConnection *conn,
+ const char *object_path);
void cras_bt_device_destroy(struct cras_bt_device *device);
void cras_bt_device_reset();
@@ -54,6 +55,18 @@ void cras_bt_device_set_append_iodev_cb(struct cras_bt_device *device,
int cras_bt_device_supports_profile(const struct cras_bt_device *device,
enum cras_bt_device_profile profile);
+/* Sets if the BT audio device should use hardware volume.
+ * Args:
+ * device - The remote bluetooth audio device.
+ * use_hardware_volume - Set to true to indicate hardware volume
+ * is preferred over software volume.
+ */
+void cras_bt_device_set_use_hardware_volume(struct cras_bt_device *device,
+ int use_hardware_volume);
+
+/* Gets if the BT audio device should use hardware volume. */
+int cras_bt_device_get_use_hardware_volume(struct cras_bt_device *device);
+
/* Forces disconnect the bt device. Used when handling audio error
* that we want to make the device be completely disconnected from
* host to reflect the state that an error has occurred.
@@ -73,13 +86,6 @@ int cras_bt_device_sco_connect(struct cras_bt_device *device);
/* Queries the preffered mtu value for SCO socket. */
int cras_bt_device_sco_mtu(struct cras_bt_device *device, int sco_socket);
-/* Sets the speaker gain for bt device, note this is for HFP/HSP mode.
- * Args:
- * device - The device object to set speaker gain.
- * gain - value between 0-100.
- */
-int cras_bt_device_set_speaker_gain(struct cras_bt_device *device, int gain);
-
/* Appends an iodev to bt device.
* Args:
* device - The device to append iodev to.
@@ -105,25 +111,25 @@ int cras_bt_device_get_active_profile(const struct cras_bt_device *device);
void cras_bt_device_set_active_profile(struct cras_bt_device *device,
unsigned int profile);
-/* Calls this function after configures the active profile of bt device
- * will reactivate the bt_ios associated with the bluetooth device. So it
- * can switch to the new active profile at open.
+/* Switches profile after the active profile of bt device has changed and
+ * enables bt iodev immediately. This function is used for profile switching
+ * at iodev open.
* Args:
* device - The bluetooth device.
* bt_iodev - The iodev triggers the reactivaion.
*/
-int cras_bt_device_switch_profile_on_open(struct cras_bt_device *device,
- struct cras_iodev *bt_iodev);
+int cras_bt_device_switch_profile_enable_dev(struct cras_bt_device *device,
+ struct cras_iodev *bt_iodev);
-/* Calls this function after configures the active profile of bt device
- * will reactivate the bt_ios associated with the bluetooth device. So it
- * can switch to the new active profile at device close.
+/* Switches profile after the active profile of bt device has changed. This
+ * function is used when we want to switch profile without changing the
+ * iodev's status.
* Args:
* device - The bluetooth device.
* bt_iodev - The iodev triggers the reactivaion.
*/
-int cras_bt_device_switch_profile_on_close(struct cras_bt_device *device,
- struct cras_iodev *bt_iodev);
+int cras_bt_device_switch_profile(struct cras_bt_device *device,
+ struct cras_iodev *bt_iodev);
/* Calls this function when the buffer size of an underlying profile iodev
* has changed and update it for the virtual bt iodev. */
@@ -139,14 +145,26 @@ int cras_bt_device_has_a2dp(struct cras_bt_device *device);
*/
int cras_bt_device_can_switch_to_a2dp(struct cras_bt_device *device);
-/* Adds an a2dp delay timer to this device. */
-void cras_bt_device_add_a2dp_delay_timer(struct cras_bt_device *device,
- struct cras_timer *timer);
+/* Updates the volume to bt_device when a volume change event is reported. */
+void cras_bt_device_update_hardware_volume(struct cras_bt_device *device,
+ int volume);
+
+/* Notifies bt_device that a2dp connection is configured. */
+void cras_bt_device_a2dp_configured(struct cras_bt_device *device);
+
+/* Cancels any scheduled suspension of device. */
+int cras_bt_device_cancel_suspend(struct cras_bt_device *device);
-/* Cancels the a2dp delay timer if it's not been trigered yet. */
-void cras_bt_device_cancel_a2dp_delay_timer(struct cras_bt_device *device);
+/* Schedules device to suspend after given delay. */
+int cras_bt_device_schedule_suspend(struct cras_bt_device *device,
+ unsigned int msec);
-/* Removes any a2dp delay timer from this device. */
-void cras_bt_device_rm_a2dp_delay_timer(struct cras_bt_device *device);
+/* Notifies bt device that audio gateway is initialized.
+ * Args:
+ * device - The bluetooth device.
+ * Returns:
+ * 0 on success, error code otherwise.
+ */
+int cras_bt_device_audio_gateway_initialized(struct cras_bt_device *device);
#endif /* CRAS_BT_DEVICE_H_ */
diff --git a/cras/src/server/cras_bt_endpoint.c b/cras/src/server/cras_bt_endpoint.c
index e3013c07..52cc3628 100644
--- a/cras/src/server/cras_bt_endpoint.c
+++ b/cras/src/server/cras_bt_endpoint.c
@@ -41,15 +41,6 @@
"</node>\n"
-static void cras_bt_endpoint_start(struct cras_bt_endpoint *endpoint,
- struct cras_bt_transport *transport)
-{
- cras_bt_transport_set_endpoint(transport, endpoint);
- endpoint->transport = transport;
-
- endpoint->start(endpoint, transport);
-}
-
static void cras_bt_endpoint_suspend(struct cras_bt_endpoint *endpoint)
{
if (!endpoint->transport)
@@ -115,7 +106,9 @@ static DBusHandlerResult cras_bt_endpoint_set_configuration(
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
- cras_bt_endpoint_start(endpoint, transport);
+ cras_bt_transport_set_endpoint(transport, endpoint);
+ endpoint->transport = transport;
+ endpoint->set_configuration(endpoint, transport);
reply = dbus_message_new_method_return(message);
if (!reply)
diff --git a/cras/src/server/cras_bt_endpoint.h b/cras/src/server/cras_bt_endpoint.h
index c9c3e33d..be56070b 100644
--- a/cras/src/server/cras_bt_endpoint.h
+++ b/cras/src/server/cras_bt_endpoint.h
@@ -24,8 +24,8 @@ struct cras_bt_endpoint {
void *capabilities, int len,
void *configuration);
- void (*start)(struct cras_bt_endpoint *endpoint,
- struct cras_bt_transport *transport);
+ void (*set_configuration)(struct cras_bt_endpoint *endpoint,
+ struct cras_bt_transport *transport);
void (*suspend)(struct cras_bt_endpoint *endpoint,
struct cras_bt_transport *transport);
diff --git a/cras/src/server/cras_bt_io.c b/cras/src/server/cras_bt_io.c
index 88c046d5..1363d90d 100644
--- a/cras/src/server/cras_bt_io.c
+++ b/cras/src/server/cras_bt_io.c
@@ -7,9 +7,10 @@
#include "cras_bt_io.h"
#include "cras_bt_device.h"
-#include "cras_dbus_util.h"
+#include "cras_utf8.h"
#include "cras_iodev.h"
#include "cras_iodev_list.h"
+#include "sfh.h"
#include "utlist.h"
#define DEFAULT_BT_DEVICE_NAME "BLUETOOTH"
@@ -66,6 +67,9 @@ static struct cras_ionode *add_profile_dev(struct cras_iodev *bt_iodev,
n->base.idx = btio->next_node_id++;
n->base.type = CRAS_NODE_TYPE_BLUETOOTH;
n->base.volume = 100;
+ n->base.stable_id = dev->info.stable_id;
+ n->base.stable_id_new = dev->info.stable_id_new;
+ n->base.max_software_gain = 0;
gettimeofday(&n->base.plugged_time, NULL);
strcpy(n->base.name, dev->info.name);
@@ -129,7 +133,7 @@ static int update_supported_formats(struct cras_iodev *iodev)
iodev->direction == CRAS_STREAM_INPUT) {
bt_switch_to_profile(btio->device,
CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY);
- cras_bt_device_switch_profile_on_open(btio->device, iodev);
+ cras_bt_device_switch_profile_enable_dev(btio->device, iodev);
return -EAGAIN;
}
@@ -206,8 +210,7 @@ static int close_dev(struct cras_iodev *iodev)
cras_bt_device_has_a2dp(btio->device)) {
cras_bt_device_set_active_profile(btio->device,
CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE);
- cras_bt_device_switch_profile_on_close(btio->device,
- iodev);
+ cras_bt_device_switch_profile(btio->device, iodev);
}
rc = dev->close_dev(dev);
@@ -223,32 +226,22 @@ static void set_bt_volume(struct cras_iodev *iodev)
if (dev->active_node)
dev->active_node->volume = iodev->active_node->volume;
- if (dev->set_volume)
- dev->set_volume(dev);
-}
-
-static int is_open(const struct cras_iodev *iodev)
-{
- struct cras_iodev *dev = active_profile_dev(iodev);
- if (!dev)
- return 0;
- return dev->is_open(dev);
-}
-static int frames_queued(const struct cras_iodev *iodev)
-{
- struct cras_iodev *dev = active_profile_dev(iodev);
- if (!dev)
- return -EINVAL;
- return dev->frames_queued(dev);
+ /* The parent bt_iodev could set software_volume_needed flag for cases
+ * that software volume provides better experience across profiles
+ * (HFP and A2DP). Otherwise, use the profile specific implementation
+ * to adjust volume. */
+ if (dev->set_volume && !iodev->software_volume_needed)
+ dev->set_volume(dev);
}
-static int dev_running(const struct cras_iodev *iodev)
+static int frames_queued(const struct cras_iodev *iodev,
+ struct timespec *tstamp)
{
struct cras_iodev *dev = active_profile_dev(iodev);
if (!dev)
return -EINVAL;
- return dev->dev_running(dev);
+ return dev->frames_queued(dev, tstamp);
}
static int delay_frames(const struct cras_iodev *iodev)
@@ -288,7 +281,8 @@ static int flush_buffer(struct cras_iodev *iodev)
/* If the first private iodev doesn't match the active profile stored on
* device, select to the correct private iodev.
*/
-static void update_active_node(struct cras_iodev *iodev, unsigned node_idx)
+static void update_active_node(struct cras_iodev *iodev, unsigned node_idx,
+ unsigned dev_enabled)
{
struct bt_io *btio = (struct bt_io *)iodev;
struct cras_ionode *node;
@@ -307,10 +301,7 @@ static void update_active_node(struct cras_iodev *iodev, unsigned node_idx)
active->profile = n->profile;
active->profile_dev = n->profile_dev;
- /* Fill all volume related stuff to/from the
- * profile dev. */
- iodev->software_volume_needed =
- active->profile_dev->software_volume_needed;
+ /* Set volume for the new profile. */
set_bt_volume(iodev);
}
}
@@ -338,11 +329,10 @@ struct cras_iodev *cras_bt_io_create(struct cras_bt_device *device,
iodev->direction = dev->direction;
strcpy(iodev->info.name, dev->info.name);
iodev->info.stable_id = dev->info.stable_id;
+ iodev->info.stable_id_new = dev->info.stable_id_new;
iodev->open_dev = open_dev;
- iodev->is_open = is_open; /* Needed by thread_add_stream */
iodev->frames_queued = frames_queued;
- iodev->dev_running = dev_running;
iodev->delay_frames = delay_frames;
iodev->get_buffer = get_buffer;
iodev->put_buffer = put_buffer;
@@ -350,8 +340,9 @@ struct cras_iodev *cras_bt_io_create(struct cras_bt_device *device,
iodev->close_dev = close_dev;
iodev->update_supported_formats = update_supported_formats;
iodev->update_active_node = update_active_node;
- iodev->software_volume_needed = dev->software_volume_needed;
+ iodev->software_volume_needed = 1;
iodev->set_volume = set_bt_volume;
+ iodev->no_stream = cras_iodev_default_no_stream_playback;
/* Create the dummy node set to plugged so it's the only node exposed
* to UI, and point it to the first profile dev. */
@@ -363,6 +354,11 @@ struct cras_iodev *cras_bt_io_create(struct cras_bt_device *device,
active->base.type = CRAS_NODE_TYPE_BLUETOOTH;
active->base.volume = 100;
active->base.plugged = 1;
+ active->base.stable_id = SuperFastHash(
+ cras_bt_device_object_path(device),
+ strlen(cras_bt_device_object_path(device)),
+ strlen(cras_bt_device_object_path(device)));
+ active->base.stable_id_new = active->base.stable_id;
active->profile = profile;
active->profile_dev = dev;
gettimeofday(&active->base.plugged_time, NULL);
@@ -450,7 +446,7 @@ int cras_bt_io_append(struct cras_iodev *bt_iodev,
cras_bt_device_can_switch_to_a2dp(btio->device)) {
bt_switch_to_profile(btio->device,
CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE);
- cras_bt_device_switch_profile_on_open(btio->device, bt_iodev);
+ cras_bt_device_switch_profile(btio->device, bt_iodev);
syslog(LOG_ERR, "Switch to A2DP on append");
}
return 0;
@@ -522,7 +518,7 @@ int cras_bt_io_remove(struct cras_iodev *bt_iodev,
/* The node of active profile could have been removed, update it.
* Return err when fail to locate the active profile dev. */
- update_active_node(bt_iodev, 0);
+ update_active_node(bt_iodev, 0, 1);
btnode = (struct bt_node *)bt_iodev->active_node;
if ((btnode->profile == 0) || (btnode->profile_dev == NULL))
return -EINVAL;
diff --git a/cras/src/server/cras_bt_manager.c b/cras/src/server/cras_bt_manager.c
index be0bd889..8d6c97a1 100644
--- a/cras/src/server/cras_bt_manager.c
+++ b/cras/src/server/cras_bt_manager.c
@@ -15,6 +15,7 @@
#include "cras_bt_adapter.h"
#include "cras_bt_device.h"
#include "cras_bt_endpoint.h"
+#include "cras_bt_player.h"
#include "cras_bt_profile.h"
#include "cras_bt_transport.h"
#include "utlist.h"
@@ -38,6 +39,7 @@ static void cras_bt_interface_added(DBusConnection *conn,
cras_bt_adapter_update_properties(
adapter, properties_array_iter, NULL);
cras_bt_register_endpoints(conn, adapter);
+ cras_bt_register_player(conn, adapter);
cras_bt_register_profiles(conn);
syslog(LOG_INFO, "Bluetooth Adapter: %s added",
@@ -57,7 +59,7 @@ static void cras_bt_interface_added(DBusConnection *conn,
cras_bt_device_update_properties(
device, properties_array_iter, NULL);
} else {
- device = cras_bt_device_create(object_path);
+ device = cras_bt_device_create(conn, object_path);
if (device) {
cras_bt_device_update_properties(
device, properties_array_iter, NULL);
diff --git a/cras/src/server/cras_bt_player.c b/cras/src/server/cras_bt_player.c
new file mode 100644
index 00000000..3821721d
--- /dev/null
+++ b/cras/src/server/cras_bt_player.c
@@ -0,0 +1,179 @@
+/* Copyright (c) 2016 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include <dbus/dbus.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include "cras_bt_constants.h"
+#include "cras_bt_adapter.h"
+#include "cras_bt_player.h"
+#include "cras_dbus_util.h"
+#include "utlist.h"
+
+#define CRAS_DEFAULT_PLAYER "/org/chromium/Cras/Bluetooth/DefaultPlayer"
+
+
+static void cras_bt_on_player_registered(DBusPendingCall *pending_call,
+ void *data)
+{
+ DBusMessage *reply;
+
+ reply = dbus_pending_call_steal_reply(pending_call);
+ dbus_pending_call_unref(pending_call);
+
+ if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+ syslog(LOG_ERR, "RegisterPlayer returned error: %s",
+ dbus_message_get_error_name(reply));
+ dbus_message_unref(reply);
+ return;
+ }
+
+ dbus_message_unref(reply);
+}
+
+static int cras_bt_add_player(DBusConnection *conn,
+ const struct cras_bt_adapter *adapter,
+ struct cras_bt_player *player)
+{
+ const char *adapter_path;
+ DBusMessage *method_call;
+ DBusMessageIter message_iter, dict;
+ DBusPendingCall *pending_call;
+
+ adapter_path = cras_bt_adapter_object_path(adapter);
+ method_call = dbus_message_new_method_call(BLUEZ_SERVICE,
+ adapter_path,
+ BLUEZ_INTERFACE_MEDIA,
+ "RegisterPlayer");
+ if (!method_call)
+ return -ENOMEM;
+
+ dbus_message_iter_init_append(method_call, &message_iter);
+ dbus_message_iter_append_basic(&message_iter,
+ DBUS_TYPE_OBJECT_PATH,
+ &player->object_path);
+
+ dbus_message_iter_open_container(&message_iter,
+ DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ append_key_value(&dict, "PlaybackStatus", DBUS_TYPE_STRING,
+ DBUS_TYPE_STRING_AS_STRING,
+ &player->playback_status);
+ append_key_value(&dict, "Identity", DBUS_TYPE_STRING,
+ DBUS_TYPE_STRING_AS_STRING,
+ &player->identity);
+ append_key_value(&dict, "LoopStatus", DBUS_TYPE_STRING,
+ DBUS_TYPE_STRING_AS_STRING,
+ &player->loop_status);
+ append_key_value(&dict, "Position", DBUS_TYPE_INT64,
+ DBUS_TYPE_INT64_AS_STRING, &player->position);
+ append_key_value(&dict, "Shuffle", DBUS_TYPE_BOOLEAN,
+ DBUS_TYPE_BOOLEAN_AS_STRING, &player->shuffle);
+ append_key_value(&dict, "CanGoNext", DBUS_TYPE_BOOLEAN,
+ DBUS_TYPE_BOOLEAN_AS_STRING, &player->can_go_next);
+ append_key_value(&dict, "CanGoPrevious", DBUS_TYPE_BOOLEAN,
+ DBUS_TYPE_BOOLEAN_AS_STRING, &player->can_go_prev);
+ append_key_value(&dict, "CanPlay", DBUS_TYPE_BOOLEAN,
+ DBUS_TYPE_BOOLEAN_AS_STRING, &player->can_play);
+ append_key_value(&dict, "CanPause", DBUS_TYPE_BOOLEAN,
+ DBUS_TYPE_BOOLEAN_AS_STRING, &player->can_pause);
+ append_key_value(&dict, "CanControl", DBUS_TYPE_BOOLEAN,
+ DBUS_TYPE_BOOLEAN_AS_STRING, &player->can_control);
+
+ dbus_message_iter_close_container(&message_iter, &dict);
+
+ if (!dbus_connection_send_with_reply(conn, method_call, &pending_call,
+ DBUS_TIMEOUT_USE_DEFAULT)) {
+ dbus_message_unref(method_call);
+ return -ENOMEM;
+ }
+
+ dbus_message_unref(method_call);
+ if (!pending_call)
+ return -EIO;
+
+ if (!dbus_pending_call_set_notify(pending_call,
+ cras_bt_on_player_registered,
+ player, NULL)) {
+ dbus_pending_call_cancel(pending_call);
+ dbus_pending_call_unref(pending_call);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+
+/* Note that player properties will be used mostly for AVRCP qualification and
+ * not for normal use cases. The corresponding media events won't be routed by
+ * CRAS until we have a plan to provide general system API to handle media
+ * control.
+ */
+static struct cras_bt_player player = {
+ .object_path = CRAS_DEFAULT_PLAYER,
+ .playback_status = "playing",
+ .identity = "DefaultPlayer",
+ .loop_status = "None",
+ .shuffle = 0,
+ .position = 0,
+ .can_go_next = 0,
+ .can_go_prev = 0,
+ .can_play = 0,
+ .can_pause = 0,
+ .can_control = 0,
+ .message_cb = NULL,
+};
+
+static DBusHandlerResult cras_bt_player_handle_message(DBusConnection *conn,
+ DBusMessage *message,
+ void *arg)
+{
+ const char *msg = dbus_message_get_member(message);
+
+ if (player.message_cb)
+ player.message_cb(msg);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+int cras_bt_player_create(DBusConnection *conn)
+{
+ static const DBusObjectPathVTable player_vtable = {
+ .message_function = cras_bt_player_handle_message
+ };
+
+ DBusError dbus_error;
+ struct cras_bt_adapter **adapters;
+ size_t num_adapters, i;
+
+ dbus_error_init(&dbus_error);
+
+ if (!dbus_connection_register_object_path(conn,
+ player.object_path,
+ &player_vtable,
+ &dbus_error)) {
+ syslog(LOG_ERR, "Cannot register player %s",
+ player.object_path);
+ dbus_error_free(&dbus_error);
+ return -ENOMEM;
+ }
+
+ num_adapters = cras_bt_adapter_get_list(&adapters);
+ for (i = 0; i < num_adapters; ++i)
+ cras_bt_add_player(conn, adapters[i], &player);
+ free(adapters);
+ return 0;
+}
+
+int cras_bt_register_player(DBusConnection *conn,
+ const struct cras_bt_adapter *adapter)
+{
+ return cras_bt_add_player(conn, adapter, &player);
+}
diff --git a/cras/src/server/cras_bt_player.h b/cras/src/server/cras_bt_player.h
new file mode 100644
index 00000000..cce3710a
--- /dev/null
+++ b/cras/src/server/cras_bt_player.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef CRAS_BT_PLAYER_H_
+#define CRAS_BT_PLAYER_H_
+
+#include <dbus/dbus.h>
+#include <stdbool.h>
+
+#include "cras_bt_adapter.h"
+
+
+/* Object to register as media player so that bluetoothd will report hardware
+ * volume from device through bt_transport. Properties of the player are defined
+ * in BlueZ's media API.
+ */
+struct cras_bt_player {
+ const char *object_path;
+ const char *playback_status;
+ const char *identity;
+ const char *loop_status;
+ int position;
+ bool can_go_next;
+ bool can_go_prev;
+ bool can_play;
+ bool can_pause;
+ bool can_control;
+ bool shuffle;
+ void (*message_cb)(const char *message);
+};
+
+/* Creates a player object and register it to bluetoothd.
+ * Args:
+ * conn - The dbus connection.
+ */
+int cras_bt_player_create(DBusConnection *conn);
+
+/* Registers created player to bluetoothd. This is used when an bluetooth
+ * adapter got enumerated.
+ * Args:
+ * conn - The dbus connection.
+ * adapter - The enumerated bluetooth adapter.
+ */
+int cras_bt_register_player(DBusConnection *conn,
+ const struct cras_bt_adapter *adapter);
+
+#endif /* CRAS_BT_PLAYER_H_ */ \ No newline at end of file
diff --git a/cras/src/server/cras_bt_profile.c b/cras/src/server/cras_bt_profile.c
index fedc344b..9f898aa0 100644
--- a/cras/src/server/cras_bt_profile.c
+++ b/cras/src/server/cras_bt_profile.c
@@ -111,7 +111,7 @@ static DBusHandlerResult cras_bt_profile_handle_new_connection(
if (!device) {
syslog(LOG_ERR, "Device %s not found at %s new connection",
object_path, profile_path);
- device = cras_bt_device_create(object_path);
+ device = cras_bt_device_create(conn, object_path);
}
err = profile->new_connection(conn, profile, device, fd);
diff --git a/cras/src/server/cras_bt_transport.c b/cras/src/server/cras_bt_transport.c
index 19274034..4037cfd4 100644
--- a/cras/src/server/cras_bt_transport.c
+++ b/cras/src/server/cras_bt_transport.c
@@ -31,6 +31,7 @@ struct cras_bt_transport {
int fd;
uint16_t read_mtu;
uint16_t write_mtu;
+ int volume;
struct cras_bt_endpoint *endpoint;
struct cras_bt_transport *prev, *next;
@@ -57,6 +58,7 @@ struct cras_bt_transport *cras_bt_transport_create(DBusConnection *conn,
dbus_connection_ref(transport->conn);
transport->fd = -1;
+ transport->volume = -1;
DL_APPEND(transports, transport);
@@ -147,11 +149,6 @@ enum cras_bt_device_profile cras_bt_transport_profile(
return transport->profile;
}
-int cras_bt_transport_codec(const struct cras_bt_transport *transport)
-{
- return transport->codec;
-}
-
int cras_bt_transport_configuration(const struct cras_bt_transport *transport,
void *configuration, int len)
{
@@ -170,22 +167,11 @@ enum cras_bt_transport_state cras_bt_transport_state(
return transport->state;
}
-struct cras_bt_endpoint *cras_bt_transport_endpoint(
- const struct cras_bt_transport *transport)
-{
- return transport->endpoint;
-}
-
int cras_bt_transport_fd(const struct cras_bt_transport *transport)
{
return transport->fd;
}
-uint16_t cras_bt_transport_read_mtu(const struct cras_bt_transport *transport)
-{
- return transport->read_mtu;
-}
-
uint16_t cras_bt_transport_write_mtu(const struct cras_bt_transport *transport)
{
return transport->write_mtu;
@@ -213,18 +199,23 @@ static void cras_bt_transport_state_changed(struct cras_bt_transport *transport)
transport);
}
-void cras_bt_transport_fill_properties(struct cras_bt_transport *transport,
- int fd, const char *uuid)
+/* Updates bt_device when certain transport property has changed. */
+static void cras_bt_transport_update_device(struct cras_bt_transport *transport)
{
- transport->device = cras_bt_device_get(transport->object_path);
- transport->profile = cras_bt_device_profile_from_uuid(uuid);
- free(transport->configuration);
+ if (!transport->device)
+ return;
- /* For HFP, the configuration is just the file descriptor of
- * the rfcomm socket */
- transport->configuration = (int *)malloc(sizeof(fd));
- memcpy(transport->configuration, &fd, sizeof(fd));
- transport->configuration_len = sizeof(fd);
+ /* When the transport has non-negaive volume, it means the remote
+ * BT audio devices supports AVRCP absolute volume. Set the flag in bt
+ * device to use hardware volume. Also map the volume value from 0-127
+ * to 0-100.
+ */
+ if (transport->volume != -1) {
+ cras_bt_device_set_use_hardware_volume(transport->device, 1);
+ cras_bt_device_update_hardware_volume(
+ transport->device,
+ transport->volume * 100 / 127);
+ }
}
void cras_bt_transport_update_properties(
@@ -285,7 +276,9 @@ void cras_bt_transport_update_properties(
"transport properties",
obj_path);
transport->device =
- cras_bt_device_create(obj_path);
+ cras_bt_device_create(transport->conn,
+ obj_path);
+ cras_bt_transport_update_device(transport);
}
} else if (strcmp(
dbus_message_iter_get_signature(&variant_iter),
@@ -308,6 +301,12 @@ void cras_bt_transport_update_properties(
transport->configuration_len = len;
}
+ } else if (strcmp(key, "Volume") == 0) {
+ uint16_t volume;
+
+ dbus_message_iter_get_basic(&variant_iter, &volume);
+ transport->volume = volume;
+ cras_bt_transport_update_device(transport);
}
dbus_message_iter_next(properties_array_iter);
@@ -338,6 +337,69 @@ void cras_bt_transport_update_properties(
}
}
+static void on_transport_volume_set(DBusPendingCall *pending_call, void *data)
+{
+ DBusMessage *reply;
+
+ reply = dbus_pending_call_steal_reply(pending_call);
+ dbus_pending_call_unref(pending_call);
+
+ if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+ syslog(LOG_ERR, "Set absolute volume returned error: %s",
+ dbus_message_get_error_name(reply));
+ dbus_message_unref(reply);
+}
+
+int cras_bt_transport_set_volume(struct cras_bt_transport *transport,
+ uint16_t volume)
+{
+ const char *key = "Volume";
+ const char *interface = BLUEZ_INTERFACE_MEDIA_TRANSPORT;
+ DBusMessage *method_call;
+ DBusMessageIter message_iter, variant;
+ DBusPendingCall *pending_call;
+
+ method_call = dbus_message_new_method_call(
+ BLUEZ_SERVICE,
+ transport->object_path,
+ DBUS_INTERFACE_PROPERTIES,
+ "Set");
+ if (!method_call)
+ return -ENOMEM;
+
+ dbus_message_iter_init_append(method_call, &message_iter);
+
+ dbus_message_iter_append_basic(&message_iter, DBUS_TYPE_STRING,
+ &interface);
+ dbus_message_iter_append_basic(&message_iter, DBUS_TYPE_STRING, &key);
+
+ dbus_message_iter_open_container(&message_iter, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_UINT16_AS_STRING, &variant);
+ dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT16, &volume);
+ dbus_message_iter_close_container(&message_iter, &variant);
+
+ if (!dbus_connection_send_with_reply(transport->conn, method_call,
+ &pending_call,
+ DBUS_TIMEOUT_USE_DEFAULT)) {
+ dbus_message_unref(method_call);
+ return -ENOMEM;
+ }
+
+ dbus_message_unref(method_call);
+ if (!pending_call)
+ return -EIO;
+
+ if (!dbus_pending_call_set_notify(pending_call,
+ on_transport_volume_set,
+ NULL, NULL)) {
+ dbus_pending_call_cancel(pending_call);
+ dbus_pending_call_unref(pending_call);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
int cras_bt_transport_acquire(struct cras_bt_transport *transport)
{
DBusMessage *method_call, *reply;
@@ -453,9 +515,30 @@ int cras_bt_transport_try_acquire(struct cras_bt_transport *transport)
return 0;
}
-int cras_bt_transport_release(struct cras_bt_transport *transport)
+/* Callback to trigger when transport release completed. */
+static void cras_bt_on_transport_release(DBusPendingCall *pending_call,
+ void *data)
+{
+ DBusMessage *reply;
+
+ reply = dbus_pending_call_steal_reply(pending_call);
+ dbus_pending_call_unref(pending_call);
+
+ if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+ syslog(LOG_WARNING, "Release transport returned error: %s",
+ dbus_message_get_error_name(reply));
+ dbus_message_unref(reply);
+ return;
+ }
+
+ dbus_message_unref(reply);
+}
+
+int cras_bt_transport_release(struct cras_bt_transport *transport,
+ unsigned int blocking)
{
DBusMessage *method_call, *reply;
+ DBusPendingCall *pending_call;
DBusError dbus_error;
if (transport->fd < 0)
@@ -475,30 +558,53 @@ int cras_bt_transport_release(struct cras_bt_transport *transport)
if (!method_call)
return -ENOMEM;
- dbus_error_init(&dbus_error);
+ if (blocking) {
+ dbus_error_init(&dbus_error);
+
+ reply = dbus_connection_send_with_reply_and_block(
+ transport->conn,
+ method_call,
+ DBUS_TIMEOUT_USE_DEFAULT,
+ &dbus_error);
+ if (!reply) {
+ syslog(LOG_ERR, "Failed to release transport %s: %s",
+ transport->object_path, dbus_error.message);
+ dbus_error_free(&dbus_error);
+ dbus_message_unref(method_call);
+ return -EIO;
+ }
- reply = dbus_connection_send_with_reply_and_block(
- transport->conn,
- method_call,
- DBUS_TIMEOUT_USE_DEFAULT,
- &dbus_error);
- if (!reply) {
- syslog(LOG_ERR, "Failed to release transport %s: %s",
- transport->object_path, dbus_error.message);
- dbus_error_free(&dbus_error);
dbus_message_unref(method_call);
- return -EIO;
- }
- dbus_message_unref(method_call);
+ if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+ syslog(LOG_ERR, "Release returned error: %s",
+ dbus_message_get_error_name(reply));
+ dbus_message_unref(reply);
+ return -EIO;
+ }
- if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
- syslog(LOG_ERR, "Release returned error: %s",
- dbus_message_get_error_name(reply));
dbus_message_unref(reply);
- return -EIO;
- }
+ } else {
+ if (!dbus_connection_send_with_reply(
+ transport->conn,
+ method_call,
+ &pending_call,
+ DBUS_TIMEOUT_USE_DEFAULT)) {
+ dbus_message_unref(method_call);
+ return -ENOMEM;
+ }
- dbus_message_unref(reply);
+ dbus_message_unref(method_call);
+ if (!pending_call)
+ return -EIO;
+
+ if (!dbus_pending_call_set_notify(pending_call,
+ cras_bt_on_transport_release,
+ transport, NULL)) {
+ dbus_pending_call_cancel(pending_call);
+ dbus_pending_call_unref(pending_call);
+ return -ENOMEM;
+ }
+ }
return 0;
}
diff --git a/cras/src/server/cras_bt_transport.h b/cras/src/server/cras_bt_transport.h
index e0737273..bafd9d20 100644
--- a/cras/src/server/cras_bt_transport.h
+++ b/cras/src/server/cras_bt_transport.h
@@ -38,28 +38,14 @@ struct cras_bt_device *cras_bt_transport_device(
const struct cras_bt_transport *transport);
enum cras_bt_device_profile cras_bt_transport_profile(
const struct cras_bt_transport *transport);
-int cras_bt_transport_codec(const struct cras_bt_transport *transport);
int cras_bt_transport_configuration(const struct cras_bt_transport *transport,
void *configuration, int len);
enum cras_bt_transport_state cras_bt_transport_state(
const struct cras_bt_transport *transport);
-struct cras_bt_endpoint *cras_bt_transport_endpoint(
- const struct cras_bt_transport *transport);
int cras_bt_transport_fd(const struct cras_bt_transport *transport);
-uint16_t cras_bt_transport_read_mtu(const struct cras_bt_transport *transport);
uint16_t cras_bt_transport_write_mtu(const struct cras_bt_transport *transport);
-/* Fills the necessary fields in cras_bt_tranport for cras_bt_profile
- * to create cras_iodev.
- * Args:
- * transport - The transport object carries the fields
- * fd - File descriptor of the rfcomm socket
- * uuid - The UUID of the profile
- */
-void cras_bt_transport_fill_properties(struct cras_bt_transport *transport,
- int fd, const char *uuid);
-
void cras_bt_transport_update_properties(
struct cras_bt_transport *transport,
DBusMessageIter *properties_array_iter,
@@ -67,6 +53,23 @@ void cras_bt_transport_update_properties(
int cras_bt_transport_try_acquire(struct cras_bt_transport *transport);
int cras_bt_transport_acquire(struct cras_bt_transport *transport);
-int cras_bt_transport_release(struct cras_bt_transport *transport);
+
+/* Releases the cras_bt_transport.
+ * Args:
+ * transport - The transport object to release
+ * blocking - True to send release dbus message in blocking mode, otherwise
+ * in non-block mode.
+ */
+int cras_bt_transport_release(struct cras_bt_transport *transport,
+ unsigned int blocking);
+
+/* Sets the volume to cras_bt_transport. Note that the volume gets applied
+ * to BT headset only when the transport is in ACTIVE state.
+ * Args:
+ * transport - The transport object to set volume to.
+ * volume - The desired volume value, range in [0-127].
+ */
+int cras_bt_transport_set_volume(struct cras_bt_transport *transport,
+ uint16_t volume);
#endif /* CRAS_BT_TRANSPORT_H_ */
diff --git a/cras/src/server/cras_dbus_control.c b/cras/src/server/cras_dbus_control.c
index 11e86e11..21002249 100644
--- a/cras/src/server/cras_dbus_control.c
+++ b/cras/src/server/cras_dbus_control.c
@@ -10,10 +10,12 @@
#include <string.h>
#include <syslog.h>
+#include "audio_thread.h"
#include "cras_dbus.h"
#include "cras_dbus_control.h"
#include "cras_dbus_util.h"
#include "cras_iodev_list.h"
+#include "cras_observer.h"
#include "cras_system_state.h"
#include "cras_util.h"
#include "utlist.h"
@@ -91,6 +93,14 @@
" <method name=\"GetNumberOfActiveInputStreams\">\n" \
" <arg name=\"num\" type=\"i\" direction=\"out\"/>\n" \
" </method>\n" \
+ " <method name=\"SetGlobalOutputChannelRemix\">\n" \
+ " <arg name=\"num_channels\" type=\"i\" direction=\"in\"/>\n" \
+ " <arg name=\"coefficient\" type=\"ad\" direction=\"in\"/>\n" \
+ " </method>\n" \
+ " <method name=\"SetHotwordModel\">\n" \
+ " <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n" \
+ " <arg name=\"model_name\" type=\"s\" direction=\"in\"/>\n" \
+ " </method>\n" \
" </interface>\n" \
" <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n" \
" <method name=\"Introspect\">\n" \
@@ -101,9 +111,9 @@
struct cras_dbus_control {
DBusConnection *conn;
+ struct cras_observer_client *observer;
};
static struct cras_dbus_control dbus_control;
-static cras_node_id_t last_output, last_input;
/* helper to extract a single argument from a DBus message. */
static int get_single_arg(DBusMessage *message, int dbus_type, void *arg)
@@ -126,7 +136,24 @@ static int get_single_arg(DBusMessage *message, int dbus_type, void *arg)
}
/* Helper to send an empty reply. */
-static void send_empty_reply(DBusMessage *message)
+static void send_empty_reply(DBusConnection *conn, DBusMessage *message)
+{
+ DBusMessage *reply;
+ dbus_uint32_t serial = 0;
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ return;
+
+ dbus_connection_send(conn, reply, &serial);
+
+ dbus_message_unref(reply);
+}
+
+/* Helper to send an int32 reply. */
+static void send_int32_reply(DBusConnection *conn,
+ DBusMessage *message,
+ dbus_int32_t value)
{
DBusMessage *reply;
dbus_uint32_t serial = 0;
@@ -135,7 +162,10 @@ static void send_empty_reply(DBusMessage *message)
if (!reply)
return;
- dbus_connection_send(dbus_control.conn, reply, &serial);
+ dbus_message_append_args(reply,
+ DBUS_TYPE_INT32, &value,
+ DBUS_TYPE_INVALID);
+ dbus_connection_send(conn, reply, &serial);
dbus_message_unref(reply);
}
@@ -155,7 +185,7 @@ static DBusHandlerResult handle_set_output_volume(
cras_system_set_volume(new_vol);
- send_empty_reply(message);
+ send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -184,7 +214,7 @@ static DBusHandlerResult handle_set_output_node_volume(
cras_iodev_list_set_node_attr(id, IONODE_ATTR_VOLUME, new_vol);
- send_empty_reply(message);
+ send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -214,7 +244,7 @@ static DBusHandlerResult handle_swap_left_right(
cras_iodev_list_set_node_attr(id, IONODE_ATTR_SWAP_LEFT_RIGHT,
swap);
- send_empty_reply(message);
+ send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -233,7 +263,7 @@ static DBusHandlerResult handle_set_output_mute(
cras_system_set_mute(new_mute);
- send_empty_reply(message);
+ send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -252,7 +282,7 @@ static DBusHandlerResult handle_set_output_user_mute(
cras_system_set_user_mute(new_mute);
- send_empty_reply(message);
+ send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -270,7 +300,7 @@ static DBusHandlerResult handle_set_suspend_audio(
cras_system_set_suspended(suspend);
- send_empty_reply(message);
+ send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -289,7 +319,7 @@ static DBusHandlerResult handle_set_input_gain(
cras_system_set_capture_gain(new_gain);
- send_empty_reply(message);
+ send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -318,7 +348,7 @@ static DBusHandlerResult handle_set_input_node_gain(
cras_iodev_list_set_node_attr(id, IONODE_ATTR_CAPTURE_GAIN, new_gain);
- send_empty_reply(message);
+ send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -337,7 +367,7 @@ static DBusHandlerResult handle_set_input_mute(
cras_system_set_capture_mute(new_mute);
- send_empty_reply(message);
+ send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -371,7 +401,7 @@ static DBusHandlerResult handle_get_volume_state(
DBUS_TYPE_BOOLEAN, &user_muted,
DBUS_TYPE_INVALID);
- dbus_connection_send(dbus_control.conn, reply, &serial);
+ dbus_connection_send(conn, reply, &serial);
dbus_message_unref(reply);
@@ -389,7 +419,8 @@ static dbus_bool_t append_node_dict(DBusMessageIter *iter,
dbus_bool_t is_input;
dbus_uint64_t id;
const char *dev_name = dev->name;
- dbus_uint64_t stable_dev_id = dev->stable_id;
+ dbus_uint64_t stable_dev_id = node->stable_id;
+ dbus_uint64_t stable_dev_id_new = node->stable_id_new;
const char *node_type = node->type;
const char *node_name = node->name;
const char *mic_positions = node->mic_positions;
@@ -398,6 +429,7 @@ static dbus_bool_t append_node_dict(DBusMessageIter *iter,
node->plugged_time.tv_usec;
dbus_uint64_t node_volume = node->volume;
dbus_int64_t node_capture_gain = node->capture_gain;
+ char *models, *empty_models = "";
is_input = (direction == CRAS_STREAM_INPUT);
id = node->iodev_idx;
@@ -419,6 +451,9 @@ static dbus_bool_t append_node_dict(DBusMessageIter *iter,
if (!append_key_value(&dict, "StableDeviceId", DBUS_TYPE_UINT64,
DBUS_TYPE_UINT64_AS_STRING, &stable_dev_id))
return FALSE;
+ if (!append_key_value(&dict, "StableDeviceIdNew", DBUS_TYPE_UINT64,
+ DBUS_TYPE_UINT64_AS_STRING, &stable_dev_id_new))
+ return FALSE;
if (!append_key_value(&dict, "Type", DBUS_TYPE_STRING,
DBUS_TYPE_STRING_AS_STRING, &node_type))
return FALSE;
@@ -440,6 +475,16 @@ static dbus_bool_t append_node_dict(DBusMessageIter *iter,
if (!append_key_value(&dict, "NodeCaptureGain", DBUS_TYPE_INT64,
DBUS_TYPE_INT64_AS_STRING, &node_capture_gain))
return FALSE;
+
+ models = cras_iodev_list_get_hotword_models(id);
+ if (!append_key_value(&dict, "HotwordModels", DBUS_TYPE_STRING,
+ DBUS_TYPE_STRING_AS_STRING,
+ models ? &models : &empty_models)) {
+ free(models);
+ return FALSE;
+ }
+ free(models);
+
if (!dbus_message_iter_close_container(iter, &dict))
return FALSE;
@@ -496,7 +541,7 @@ static DBusHandlerResult handle_get_nodes(DBusConnection *conn,
return DBUS_HANDLER_RESULT_NEED_MEMORY;
if (!append_nodes(CRAS_STREAM_INPUT, &array))
return DBUS_HANDLER_RESULT_NEED_MEMORY;
- dbus_connection_send(dbus_control.conn, reply, &serial);
+ dbus_connection_send(conn, reply, &serial);
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
@@ -517,7 +562,7 @@ handle_set_active_node(DBusConnection *conn,
cras_iodev_list_select_node(direction, id);
- send_empty_reply(message);
+ send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -537,7 +582,7 @@ handle_add_active_node(DBusConnection *conn,
cras_iodev_list_add_active_node(direction, id);
- send_empty_reply(message);
+ send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -557,7 +602,7 @@ handle_rm_active_node(DBusConnection *conn,
cras_iodev_list_rm_active_node(direction, id);
- send_empty_reply(message);
+ send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -567,18 +612,7 @@ static DBusHandlerResult handle_get_num_active_streams(
DBusMessage *message,
void *arg)
{
- DBusMessage *reply;
- dbus_uint32_t serial = 0;
- dbus_int32_t num;
-
- reply = dbus_message_new_method_return(message);
- num = cras_system_state_get_active_streams();
- dbus_message_append_args(reply,
- DBUS_TYPE_INT32, &num,
- DBUS_TYPE_INVALID);
- dbus_connection_send(dbus_control.conn, reply, &serial);
- dbus_message_unref(reply);
-
+ send_int32_reply(conn, message, cras_system_state_get_active_streams());
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -587,23 +621,14 @@ static DBusHandlerResult handle_get_num_active_streams_use_input_hw(
DBusMessage *message,
void *arg)
{
- DBusMessage *reply;
- dbus_uint32_t serial = 0;
dbus_int32_t num = 0;
unsigned i;
- reply = dbus_message_new_method_return(message);
-
for (i = 0; i < CRAS_NUM_DIRECTIONS; i++) {
if (cras_stream_uses_input_hw(i))
num += cras_system_state_get_active_streams_by_direction(i);
}
-
- dbus_message_append_args(reply,
- DBUS_TYPE_INT32, &num,
- DBUS_TYPE_INVALID);
- dbus_connection_send(dbus_control.conn, reply, &serial);
- dbus_message_unref(reply);
+ send_int32_reply(conn, message, num);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -613,23 +638,85 @@ static DBusHandlerResult handle_get_num_active_streams_use_output_hw(
DBusMessage *message,
void *arg)
{
- DBusMessage *reply;
- dbus_uint32_t serial = 0;
dbus_int32_t num = 0;
unsigned i;
- reply = dbus_message_new_method_return(message);
-
for (i = 0; i < CRAS_NUM_DIRECTIONS; i++) {
if (cras_stream_uses_output_hw(i))
num += cras_system_state_get_active_streams_by_direction(i);
}
+ send_int32_reply(conn, message, num);
- dbus_message_append_args(reply,
- DBUS_TYPE_INT32, &num,
- DBUS_TYPE_INVALID);
- dbus_connection_send(dbus_control.conn, reply, &serial);
- dbus_message_unref(reply);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult handle_set_global_output_channel_remix(
+ DBusConnection *conn,
+ DBusMessage *message,
+ void *arg)
+{
+ dbus_int32_t num_channels;
+ double *coeff_array;
+ dbus_int32_t count;
+ DBusError dbus_error;
+ float *coefficient;
+ int i;
+
+ dbus_error_init(&dbus_error);
+
+ if (!dbus_message_get_args(message, &dbus_error,
+ DBUS_TYPE_INT32, &num_channels,
+ DBUS_TYPE_ARRAY,
+ DBUS_TYPE_DOUBLE, &coeff_array, &count,
+ DBUS_TYPE_INVALID)) {
+ syslog(LOG_WARNING, "Set global output channel remix error: %s",
+ dbus_error.message);
+ dbus_error_free(&dbus_error);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ coefficient = (float *)calloc(count, sizeof(*coefficient));
+ if (!coefficient)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ for (i = 0; i < count; i++)
+ coefficient[i] = coeff_array[i];
+
+ audio_thread_config_global_remix(
+ cras_iodev_list_get_audio_thread(),
+ num_channels,
+ coefficient);
+
+ send_empty_reply(conn, message);
+ free(coefficient);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult handle_set_hotword_model(
+ DBusConnection *conn,
+ DBusMessage *message,
+ void *arg)
+{
+ cras_node_id_t id;
+ const char *model_name;
+ DBusError dbus_error;
+ dbus_int32_t ret;
+
+ dbus_error_init(&dbus_error);
+
+ if (!dbus_message_get_args(message, &dbus_error,
+ DBUS_TYPE_UINT64, &id,
+ DBUS_TYPE_STRING, &model_name,
+ DBUS_TYPE_INVALID)) {
+ syslog(LOG_WARNING,
+ "Bad method received: %s",
+ dbus_error.message);
+ dbus_error_free(&dbus_error);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ ret = cras_iodev_list_set_hotword_model(id, model_name);
+ send_int32_reply(conn, message, ret);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -751,8 +838,18 @@ static DBusHandlerResult handle_control_message(DBusConnection *conn,
"GetNumberOfActiveOutputStreams")) {
return handle_get_num_active_streams_use_output_hw(
conn, message, arg);
+ } else if (dbus_message_is_method_call(message,
+ CRAS_CONTROL_INTERFACE,
+ "SetGlobalOutputChannelRemix")) {
+ return handle_set_global_output_channel_remix(
+ conn, message, arg);
+ } else if (dbus_message_is_method_call(message,
+ CRAS_CONTROL_INTERFACE,
+ "SetHotwordModel")) {
+ return handle_set_hotword_model(conn, message, arg);
}
+
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
@@ -771,11 +868,11 @@ static DBusMessage *create_dbus_message(const char *name)
/* Handlers for system updates that generate DBus signals. */
-static void signal_volume(void *arg)
+static void signal_output_volume(void *context, int32_t volume)
{
+ struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
- dbus_int32_t volume;
msg = create_dbus_message("OutputVolumeChanged");
if (!msg)
@@ -785,16 +882,16 @@ static void signal_volume(void *arg)
dbus_message_append_args(msg,
DBUS_TYPE_INT32, &volume,
DBUS_TYPE_INVALID);
- dbus_connection_send(dbus_control.conn, msg, &serial);
+ dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
-static void signal_mute(void *arg)
+static void signal_output_mute(void *context, int muted, int user_muted,
+ int mute_locked)
{
+ struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
- dbus_bool_t muted;
- dbus_bool_t user_muted;
msg = create_dbus_message("OutputMuteChanged");
if (!msg)
@@ -806,48 +903,47 @@ static void signal_mute(void *arg)
DBUS_TYPE_BOOLEAN, &muted,
DBUS_TYPE_BOOLEAN, &user_muted,
DBUS_TYPE_INVALID);
- dbus_connection_send(dbus_control.conn, msg, &serial);
+ dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
-static void signal_capture_gain(void *arg)
+static void signal_capture_gain(void *context, int32_t gain)
{
+ struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
- dbus_int32_t gain;
msg = create_dbus_message("InputGainChanged");
if (!msg)
return;
- gain = cras_system_get_capture_gain();
dbus_message_append_args(msg,
DBUS_TYPE_INT32, &gain,
DBUS_TYPE_INVALID);
- dbus_connection_send(dbus_control.conn, msg, &serial);
+ dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
-static void signal_capture_mute(void *arg)
+static void signal_capture_mute(void *context, int muted, int mute_locked)
{
+ struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
- dbus_bool_t muted;
msg = create_dbus_message("InputMuteChanged");
if (!msg)
return;
- muted = cras_system_get_capture_mute();
dbus_message_append_args(msg,
DBUS_TYPE_BOOLEAN, &muted,
DBUS_TYPE_INVALID);
- dbus_connection_send(dbus_control.conn, msg, &serial);
+ dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
-static void signal_nodes_changed(void *arg)
+static void signal_nodes_changed(void *context)
{
+ struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
@@ -855,112 +951,96 @@ static void signal_nodes_changed(void *arg)
if (!msg)
return;
- dbus_connection_send(dbus_control.conn, msg, &serial);
+ dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
-static void signal_with_node_id(const char *name, cras_node_id_t id)
+static void signal_active_node_changed(void *context,
+ enum CRAS_STREAM_DIRECTION dir,
+ cras_node_id_t node_id)
{
+ struct cras_dbus_control *control = (struct cras_dbus_control *)context;
DBusMessage *msg;
dbus_uint32_t serial = 0;
- msg = create_dbus_message(name);
+ msg = create_dbus_message((dir == CRAS_STREAM_OUTPUT)
+ ? "ActiveOutputNodeChanged"
+ : "ActiveInputNodeChanged");
if (!msg)
return;
dbus_message_append_args(msg,
- DBUS_TYPE_UINT64, &id,
+ DBUS_TYPE_UINT64, &node_id,
DBUS_TYPE_INVALID);
- dbus_connection_send(dbus_control.conn, msg, &serial);
+ dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
-static void signal_active_node_changed(void *arg)
-{
- cras_node_id_t output, input;
-
- output = cras_iodev_list_get_active_node_id(CRAS_STREAM_OUTPUT);
- input = cras_iodev_list_get_active_node_id(CRAS_STREAM_INPUT);
-
- if (last_output != output) {
- last_output = output;
- signal_with_node_id("ActiveOutputNodeChanged", output);
- }
-
- if (last_input != input) {
- last_input = input;
- signal_with_node_id("ActiveInputNodeChanged", input);
- }
-}
-
/* Called by iodev_list when a node volume changes. */
-static void signal_node_volume_changed(cras_node_id_t id,
- int volume)
+static void signal_node_volume_changed(void *context,
+ cras_node_id_t node_id,
+ int32_t volume)
{
+ struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
- dbus_uint64_t node_id;
- dbus_int32_t node_volume;
msg = create_dbus_message("OutputNodeVolumeChanged");
if (!msg)
return;
- node_id = id;
- node_volume = volume;
dbus_message_append_args(msg,
DBUS_TYPE_UINT64, &node_id,
- DBUS_TYPE_INT32, &node_volume,
+ DBUS_TYPE_INT32, &volume,
DBUS_TYPE_INVALID);
- dbus_connection_send(dbus_control.conn, msg, &serial);
+ dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
-static void signal_node_capture_gain_changed(cras_node_id_t id,
+static void signal_node_capture_gain_changed(void *context,
+ cras_node_id_t node_id,
int capture_gain)
{
+ struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
- dbus_uint64_t node_id;
- dbus_int32_t node_capture_gain;
msg = create_dbus_message("InputNodeGainChanged");
if (!msg)
return;
- node_id = id;
- node_capture_gain = capture_gain;
dbus_message_append_args(msg,
DBUS_TYPE_UINT64, &node_id,
- DBUS_TYPE_INT32, &node_capture_gain,
+ DBUS_TYPE_INT32, &capture_gain,
DBUS_TYPE_INVALID);
- dbus_connection_send(dbus_control.conn, msg, &serial);
+ dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
-static void signal_node_left_right_swapped_changed(cras_node_id_t id,
- int swapped)
+static void signal_node_left_right_swapped_changed(void *context,
+ cras_node_id_t node_id,
+ int swapped)
{
+ struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
- dbus_uint64_t node_id;
- dbus_bool_t node_left_right_swapped;
msg = create_dbus_message("NodeLeftRightSwappedChanged");
if (!msg)
return;
- node_id = id;
- node_left_right_swapped = swapped;
dbus_message_append_args(msg,
DBUS_TYPE_UINT64, &node_id,
- DBUS_TYPE_BOOLEAN, &node_left_right_swapped,
+ DBUS_TYPE_BOOLEAN, &swapped,
DBUS_TYPE_INVALID);
- dbus_connection_send(dbus_control.conn, msg, &serial);
+ dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
-static void signal_num_active_streams_changed(void *arg)
+static void signal_num_active_streams_changed(void *context,
+ enum CRAS_STREAM_DIRECTION dir,
+ uint32_t num_active_streams)
{
+ struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
dbus_int32_t num;
@@ -973,7 +1053,7 @@ static void signal_num_active_streams_changed(void *arg)
dbus_message_append_args(msg,
DBUS_TYPE_INT32, &num,
DBUS_TYPE_INVALID);
- dbus_connection_send(dbus_control.conn, msg, &serial);
+ dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
@@ -986,6 +1066,7 @@ void cras_dbus_control_start(DBusConnection *conn)
};
DBusError dbus_error;
+ struct cras_observer_ops observer_ops;
dbus_control.conn = conn;
dbus_connection_ref(dbus_control.conn);
@@ -1001,19 +1082,21 @@ void cras_dbus_control_start(DBusConnection *conn)
return;
}
- cras_system_register_volume_changed_cb(signal_volume, 0);
- cras_system_register_mute_changed_cb(signal_mute, 0);
- cras_system_register_capture_gain_changed_cb(signal_capture_gain, 0);
- cras_system_register_capture_mute_changed_cb(signal_capture_mute, 0);
- cras_system_register_active_streams_changed_cb(
- signal_num_active_streams_changed, 0);
- cras_iodev_list_register_nodes_changed_cb(signal_nodes_changed, 0);
- cras_iodev_list_register_active_node_changed_cb(
- signal_active_node_changed, 0);
- cras_iodev_list_set_node_volume_callbacks(
- signal_node_volume_changed, signal_node_capture_gain_changed);
- cras_iodev_list_set_node_left_right_swapped_callbacks(
- signal_node_left_right_swapped_changed);
+ memset(&observer_ops, 0, sizeof(observer_ops));
+ observer_ops.output_volume_changed = signal_output_volume;
+ observer_ops.output_mute_changed = signal_output_mute;
+ observer_ops.capture_gain_changed = signal_capture_gain;
+ observer_ops.capture_mute_changed = signal_capture_mute;
+ observer_ops.num_active_streams_changed =
+ signal_num_active_streams_changed;
+ observer_ops.nodes_changed = signal_nodes_changed;
+ observer_ops.active_node_changed = signal_active_node_changed;
+ observer_ops.input_node_gain_changed = signal_node_capture_gain_changed;
+ observer_ops.output_node_volume_changed = signal_node_volume_changed;
+ observer_ops.node_left_right_swapped_changed =
+ signal_node_left_right_swapped_changed;
+
+ dbus_control.observer = cras_observer_add(&observer_ops, &dbus_control);
}
void cras_dbus_control_stop()
@@ -1021,19 +1104,11 @@ void cras_dbus_control_stop()
if (!dbus_control.conn)
return;
- cras_system_remove_volume_changed_cb(signal_volume, 0);
- cras_system_remove_mute_changed_cb(signal_mute, 0);
- cras_system_remove_capture_gain_changed_cb(signal_capture_gain, 0);
- cras_system_remove_capture_mute_changed_cb(signal_capture_mute, 0);
- cras_system_remove_active_streams_changed_cb(
- signal_num_active_streams_changed, 0);
- cras_iodev_list_remove_nodes_changed_cb(signal_nodes_changed, 0);
- cras_iodev_list_remove_active_node_changed_cb(
- signal_active_node_changed, 0);
-
dbus_connection_unregister_object_path(dbus_control.conn,
CRAS_ROOT_OBJECT_PATH);
dbus_connection_unref(dbus_control.conn);
dbus_control.conn = NULL;
+ cras_observer_remove(dbus_control.observer);
+ dbus_control.observer = NULL;
}
diff --git a/cras/src/server/cras_dbus_util.c b/cras/src/server/cras_dbus_util.c
index 696d0afb..b2042797 100644
--- a/cras/src/server/cras_dbus_util.c
+++ b/cras/src/server/cras_dbus_util.c
@@ -28,8 +28,3 @@ dbus_bool_t append_key_value(DBusMessageIter *iter, const char *key,
return TRUE;
}
-
-int is_utf8_string(const char* string)
-{
- return !!dbus_validate_utf8(string, NULL);
-}
diff --git a/cras/src/server/cras_dbus_util.h b/cras/src/server/cras_dbus_util.h
index 7e6368da..6e9215a3 100644
--- a/cras/src/server/cras_dbus_util.h
+++ b/cras/src/server/cras_dbus_util.h
@@ -18,11 +18,3 @@
dbus_bool_t append_key_value(DBusMessageIter *iter, const char *key,
int type, const char *type_string,
void *value);
-
-/* Checks if a string is a valid utf-8 string.
- * Args:
- * string - a string.
- * Returns:
- * 1 if it is a valid utf-8 string. 0 otherwise.
-*/
-int is_utf8_string(const char* string);
diff --git a/cras/src/server/cras_device_monitor.c b/cras/src/server/cras_device_monitor.c
new file mode 100644
index 00000000..be9d0868
--- /dev/null
+++ b/cras/src/server/cras_device_monitor.c
@@ -0,0 +1,101 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <syslog.h>
+
+#include "cras_device_monitor.h"
+#include "cras_iodev_list.h"
+#include "cras_main_message.h"
+
+enum CRAS_DEVICE_MONITOR_MSG_TYPE {
+ RESET_DEVICE,
+ SET_MUTE_STATE,
+};
+
+struct cras_device_monitor_message {
+ struct cras_main_message header;
+ enum CRAS_DEVICE_MONITOR_MSG_TYPE message_type;
+ struct cras_iodev *iodev;
+};
+
+static void init_device_msg(
+ struct cras_device_monitor_message *msg,
+ enum CRAS_DEVICE_MONITOR_MSG_TYPE type,
+ struct cras_iodev *iodev)
+{
+ memset(msg, 0, sizeof(*msg));
+ msg->header.type = CRAS_MAIN_MONITOR_DEVICE;
+ msg->header.length = sizeof(*msg);
+ msg->message_type = type;
+ msg->iodev = iodev;
+}
+
+int cras_device_monitor_reset_device(struct cras_iodev *iodev)
+{
+ struct cras_device_monitor_message msg;
+ int err;
+
+ init_device_msg(&msg, RESET_DEVICE, iodev);
+ err = cras_main_message_send((struct cras_main_message *)&msg);
+ if (err < 0) {
+ syslog(LOG_ERR, "Failed to send device message %d",
+ RESET_DEVICE);
+ return err;
+ }
+ return 0;
+}
+
+int cras_device_monitor_set_device_mute_state(struct cras_iodev *iodev)
+{
+ struct cras_device_monitor_message msg;
+ int err;
+
+ init_device_msg(&msg, SET_MUTE_STATE, iodev);
+ err = cras_main_message_send((struct cras_main_message *)&msg);
+ if (err < 0) {
+ syslog(LOG_ERR, "Failed to send device message %d",
+ SET_MUTE_STATE);
+ return err;
+ }
+ return 0;
+}
+
+
+/* When device is in a bad state, e.g. severe underrun,
+ * it might break how audio thread works and cause busy wake up loop.
+ * Resetting the device can bring device back to normal state.
+ * Let main thread follow the disable/enable sequence in iodev_list
+ * to properly close/open the device while enabling/disabling fallback
+ * device.
+ */
+static void handle_device_message(struct cras_main_message *msg, void *arg)
+{
+ struct cras_device_monitor_message *device_msg =
+ (struct cras_device_monitor_message *)msg;
+ struct cras_iodev *iodev = device_msg->iodev;
+
+ switch (device_msg->message_type) {
+ case RESET_DEVICE:
+ syslog(LOG_ERR, "trying to recover device 0x%x by resetting it",
+ iodev->info.idx);
+ cras_iodev_list_disable_dev(iodev);
+ cras_iodev_list_enable_dev(iodev);
+ break;
+ case SET_MUTE_STATE:
+ cras_iodev_set_mute(iodev);
+ break;
+ default:
+ syslog(LOG_ERR, "Unknown device message type %u",
+ device_msg->message_type);
+ break;
+ }
+}
+
+int cras_device_monitor_init()
+{
+ cras_main_message_add_handler(CRAS_MAIN_MONITOR_DEVICE,
+ handle_device_message, NULL);
+ return 0;
+}
diff --git a/cras/src/server/cras_device_monitor.h b/cras/src/server/cras_device_monitor.h
new file mode 100644
index 00000000..21aa9f6d
--- /dev/null
+++ b/cras/src/server/cras_device_monitor.h
@@ -0,0 +1,20 @@
+/* Copyright 2016 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_DEVICE_MONITOR_H_
+#define CRAS_DEVICE_MONITOR_H_
+
+#include "cras_iodev.h"
+
+/* Asks main thread to reset a device */
+int cras_device_monitor_reset_device(struct cras_iodev *iodev);
+
+/* Asks main thread to set mute/unmute state on a device. */
+int cras_device_monitor_set_device_mute_state(struct cras_iodev *iodev);
+
+/* Initializes device monitor and sets main thread callback. */
+int cras_device_monitor_init();
+
+#endif /* CRAS_DEVICE_MONITOR_H_ */
diff --git a/cras/src/server/cras_dsp.c b/cras/src/server/cras_dsp.c
index 99d00e34..8052a047 100644
--- a/cras/src/server/cras_dsp.c
+++ b/cras/src/server/cras_dsp.c
@@ -45,6 +45,7 @@ static void initialize_environment(struct cras_expr_env *env)
cras_expr_env_set_variable_boolean(env, "disable_eq", 0);
cras_expr_env_set_variable_boolean(env, "disable_drc", 0);
cras_expr_env_set_variable_string(env, "dsp_name", "");
+ cras_expr_env_set_variable_boolean(env, "swap_lr_disabled", 1);
}
static struct pipeline *prepare_pipeline(struct cras_dsp_context *ctx)
@@ -58,9 +59,9 @@ static struct pipeline *prepare_pipeline(struct cras_dsp_context *ctx)
pipeline = cras_dsp_pipeline_create(ini, &ctx->env, purpose);
if (pipeline) {
- syslog(LOG_ERR, "pipeline created");
+ syslog(LOG_DEBUG, "pipeline created");
} else {
- syslog(LOG_ERR, "cannot create pipeline");
+ syslog(LOG_DEBUG, "cannot create pipeline");
goto bail;
}
@@ -170,12 +171,19 @@ void cras_dsp_context_free(struct cras_dsp_context *ctx)
free(ctx);
}
-void cras_dsp_set_variable(struct cras_dsp_context *ctx, const char *key,
+void cras_dsp_set_variable_string(struct cras_dsp_context *ctx, const char *key,
const char *value)
{
cras_expr_env_set_variable_string(&ctx->env, key, value);
}
+void cras_dsp_set_variable_boolean(struct cras_dsp_context *ctx,
+ const char *key,
+ char value)
+{
+ cras_expr_env_set_variable_boolean(&ctx->env, key, value);
+}
+
void cras_dsp_load_pipeline(struct cras_dsp_context *ctx)
{
cmd_load_pipeline(ctx);
diff --git a/cras/src/server/cras_dsp.h b/cras/src/server/cras_dsp.h
index fa2fd95a..18c45f7a 100644
--- a/cras/src/server/cras_dsp.h
+++ b/cras/src/server/cras_dsp.h
@@ -40,9 +40,13 @@ struct cras_dsp_context *cras_dsp_context_new(int sample_rate,
/* Frees a dsp context. */
void cras_dsp_context_free(struct cras_dsp_context *ctx);
-/* Sets a configuration variable in the context. */
-void cras_dsp_set_variable(struct cras_dsp_context *ctx, const char *key,
- const char *value);
+/* Sets a configuration string variable in the context. */
+void cras_dsp_set_variable_string(struct cras_dsp_context *ctx, const char *key,
+ const char *value);
+
+/* Sets a configuration boolean variable in the context. */
+void cras_dsp_set_variable_boolean(struct cras_dsp_context *ctx, const char *key,
+ char value);
/* Loads the pipeline to the context. This should be called again when
* new values of configuration variables may change the plugin
@@ -72,19 +76,6 @@ unsigned int cras_dsp_num_output_channels(const struct cras_dsp_context *ctx);
/* Number of channels input. */
unsigned int cras_dsp_num_input_channels(const struct cras_dsp_context *ctx);
-/* Wait for the previous asynchronous requests to finish. The
- * asynchronous requests include:
- *
- * cras_dsp_context_free()
- * cras_dsp_set_variable()
- * cras_dsp_load_pipeline()
- * cras_dsp_reload_ini()
- * cras_dsp_dump_info()
- *
- * This is mainly used for testing.
- */
-void cras_dsp_sync();
-
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/cras/src/server/cras_dsp_ini.c b/cras/src/server/cras_dsp_ini.c
index b34a1989..a4014c8a 100644
--- a/cras/src/server/cras_dsp_ini.c
+++ b/cras/src/server/cras_dsp_ini.c
@@ -3,6 +3,7 @@
* found in the LICENSE file.
*/
+#include <errno.h>
#include <stdlib.h>
#include <syslog.h>
#include "cras_dsp_ini.h"
@@ -192,6 +193,124 @@ static void fill_flow_info(struct ini *ini)
}
}
+/* Adds a port to a plugin with specified flow id and direction. */
+static void add_audio_port(struct ini *ini,
+ struct plugin *plugin,
+ int flow_id,
+ enum port_direction port_direction)
+{
+ struct port *p;
+ p = ARRAY_APPEND_ZERO(&plugin->ports);
+ p->type = PORT_AUDIO;
+ p->flow_id = flow_id;
+ p->init_value = 0;
+ p->direction = port_direction;
+}
+
+/* Fills fields for a swap_lr plugin.*/
+static void fill_swap_lr_plugin(struct ini *ini,
+ struct plugin *plugin,
+ int input_flowid_0,
+ int input_flowid_1,
+ int output_flowid_0,
+ int output_flowid_1)
+{
+ plugin->title = "swap_lr";
+ plugin->library = "builtin";
+ plugin->label = "swap_lr";
+ plugin->purpose = "playback";
+ plugin->disable_expr = cras_expr_expression_parse("swap_lr_disabled");
+
+ add_audio_port(ini, plugin, input_flowid_0, PORT_INPUT);
+ add_audio_port(ini, plugin, input_flowid_1, PORT_INPUT);
+ add_audio_port(ini, plugin, output_flowid_0, PORT_OUTPUT);
+ add_audio_port(ini, plugin, output_flowid_1, PORT_OUTPUT);
+}
+
+/* Adds a new flow with name. If there is already a flow with the name, returns
+ * INVALID_FLOW_ID.
+ */
+static int add_new_flow(struct ini *ini, const char *name)
+{
+ struct flow *flow;
+ int i = lookup_flow(ini, name);
+ if (i != -1)
+ return INVALID_FLOW_ID;
+ i = ARRAY_COUNT(&ini->flows);
+ flow = ARRAY_APPEND_ZERO(&ini->flows);
+ flow->name = name;
+ return i;
+}
+
+/* Finds the first playback sink plugin in ini. */
+struct plugin *find_first_playback_sink_plugin(struct ini *ini)
+{
+ int i;
+ struct plugin *plugin;
+
+ FOR_ARRAY_ELEMENT(&ini->plugins, i, plugin) {
+ if (strcmp(plugin->library, "builtin") != 0)
+ continue;
+ if (strcmp(plugin->label, "sink") != 0)
+ continue;
+ if (!plugin->purpose ||
+ strcmp(plugin->purpose, "playback") != 0)
+ continue;
+ return plugin;
+ }
+
+ return NULL;
+}
+
+/* Inserts a swap_lr plugin before sink. Handles the port change such that
+ * the port originally connects to sink will connect to swap_lr.
+ */
+static int insert_swap_lr_plugin(struct ini *ini)
+{
+ struct plugin *swap_lr, *sink;
+ int sink_input_flowid_0, sink_input_flowid_1;
+ int swap_lr_output_flowid_0, swap_lr_output_flowid_1;
+
+ /* Only add swap_lr plugin for two-channel playback dsp.
+ * TODO(cychiang): Handle multiple sinks if needed.
+ */
+ sink = find_first_playback_sink_plugin(ini);
+ if ((sink == NULL) || ARRAY_COUNT(&sink->ports) != 2)
+ return 0;
+
+ /* Gets the original flow ids of the sink input ports. */
+ sink_input_flowid_0 = ARRAY_ELEMENT(&sink->ports, 0)->flow_id;
+ sink_input_flowid_1 = ARRAY_ELEMENT(&sink->ports, 1)->flow_id;
+
+ /* Create new flow ids for swap_lr output ports. */
+ swap_lr_output_flowid_0 = add_new_flow(ini, "{swap_lr_out:0}");
+ swap_lr_output_flowid_1 = add_new_flow(ini, "{swap_lr_out:1}");
+
+ if (swap_lr_output_flowid_0 == INVALID_FLOW_ID ||
+ swap_lr_output_flowid_1 == INVALID_FLOW_ID) {
+ syslog(LOG_ERR, "Can not create flow id for swap_lr_out");
+ return -EINVAL;
+ }
+
+ /* Creates a swap_lr plugin and sets the input and output ports. */
+ swap_lr = ARRAY_APPEND_ZERO(&ini->plugins);
+ fill_swap_lr_plugin(ini,
+ swap_lr,
+ sink_input_flowid_0,
+ sink_input_flowid_1,
+ swap_lr_output_flowid_0,
+ swap_lr_output_flowid_1);
+
+ /* Look up first sink again because ini->plugins could be realloc'ed */
+ sink = find_first_playback_sink_plugin(ini);
+
+ /* The flow ids of sink input ports should be changed to flow ids of
+ * {swap_lr_out:0}, {swap_lr_out:1}. */
+ ARRAY_ELEMENT(&sink->ports, 0)->flow_id = swap_lr_output_flowid_0;
+ ARRAY_ELEMENT(&sink->ports, 1)->flow_id = swap_lr_output_flowid_1;
+
+ return 0;
+}
struct ini *cras_dsp_ini_create(const char *ini_filename)
{
@@ -200,6 +319,7 @@ struct ini *cras_dsp_ini_create(const char *ini_filename)
int nsec, i;
const char *sec_name;
struct plugin *plugin;
+ int rc;
ini = calloc(1, sizeof(struct ini));
if (!ini) {
@@ -223,6 +343,13 @@ struct ini *cras_dsp_ini_create(const char *ini_filename)
goto bail;
}
+ /* Insert a swap_lr plugin before sink. */
+ rc = insert_swap_lr_plugin(ini);
+ if (rc < 0) {
+ syslog(LOG_ERR, "failed to insert swap_lr plugin");
+ goto bail;
+ }
+
/* Fill flow info now because now the plugin array won't change */
fill_flow_info(ini);
diff --git a/cras/src/server/cras_dsp_mod_builtin.c b/cras/src/server/cras_dsp_mod_builtin.c
index de93cb08..c6948795 100644
--- a/cras/src/server/cras_dsp_mod_builtin.c
+++ b/cras/src/server/cras_dsp_mod_builtin.c
@@ -56,6 +56,56 @@ static void empty_init_module(struct dsp_module *module)
}
/*
+ * swap_lr module functions
+ */
+static int swap_lr_instantiate(struct dsp_module *module,
+ unsigned long sample_rate)
+{
+ module->data = calloc(4, sizeof(float*));
+ return 0;
+}
+
+static void swap_lr_connect_port(struct dsp_module *module,
+ unsigned long port, float *data_location)
+{
+ float **ports;
+ ports = (float **)module->data;
+ ports[port] = data_location;
+}
+
+static void swap_lr_run(struct dsp_module *module,
+ unsigned long sample_count)
+{
+ size_t i;
+ float **ports = (float **)module->data;
+
+ /* This module runs dsp in-place, so ports[0] == ports[2],
+ * ports[1] == ports[3]. Here we swap data on two channels.
+ */
+ for (i = 0; i < sample_count; i++) {
+ float temp = ports[0][i];
+ ports[2][i] = ports[1][i];
+ ports[3][i] = temp;
+ }
+}
+
+static void swap_lr_deinstantiate(struct dsp_module *module)
+{
+ free(module->data);
+}
+
+static void swap_lr_init_module(struct dsp_module *module)
+{
+ module->instantiate = &swap_lr_instantiate;
+ module->connect_port = &swap_lr_connect_port;
+ module->get_delay = &empty_get_delay;
+ module->run = &swap_lr_run;
+ module->deinstantiate = &swap_lr_deinstantiate;
+ module->free_module = &empty_free_module;
+ module->get_properties = &empty_get_properties;
+}
+
+/*
* invert_lr module functions
*/
static int invert_lr_instantiate(struct dsp_module *module,
@@ -424,6 +474,8 @@ struct dsp_module *cras_dsp_module_load_builtin(struct plugin *plugin)
eq2_init_module(module);
} else if (strcmp(plugin->label, "drc") == 0) {
drc_init_module(module);
+ } else if (strcmp(plugin->label, "swap_lr") == 0) {
+ swap_lr_init_module(module);
} else {
empty_init_module(module);
}
diff --git a/cras/src/server/cras_dsp_pipeline.c b/cras/src/server/cras_dsp_pipeline.c
index f05c936d..332ed1c0 100644
--- a/cras/src/server/cras_dsp_pipeline.c
+++ b/cras/src/server/cras_dsp_pipeline.c
@@ -412,7 +412,7 @@ struct pipeline *cras_dsp_pipeline_create(struct ini *ini,
ini, "sink", purpose, env);
if (!source || !sink) {
- syslog(LOG_INFO,
+ syslog(LOG_DEBUG,
"no enabled source or sink found %p/%p for %s",
source, sink, purpose);
return NULL;
diff --git a/cras/src/server/cras_empty_iodev.c b/cras/src/server/cras_empty_iodev.c
index ef592093..4616cd3c 100644
--- a/cras/src/server/cras_empty_iodev.c
+++ b/cras/src/server/cras_empty_iodev.c
@@ -70,20 +70,10 @@ static unsigned int current_level(const struct cras_iodev *iodev)
* iodev callbacks.
*/
-static int is_open(const struct cras_iodev *iodev)
-{
- struct empty_iodev *empty_iodev = (struct empty_iodev *)iodev;
-
- return empty_iodev->open;
-}
-
-static int dev_running(const struct cras_iodev *iodev)
-{
- return 1;
-}
-
-static int frames_queued(const struct cras_iodev *iodev)
+static int frames_queued(const struct cras_iodev *iodev,
+ struct timespec *tstamp)
{
+ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
return current_level(iodev);
}
@@ -125,11 +115,15 @@ static int get_buffer(struct cras_iodev *iodev,
unsigned *frames)
{
struct empty_iodev *empty_iodev = (struct empty_iodev *)iodev;
+ unsigned int avail, current;
- if (iodev->direction == CRAS_STREAM_OUTPUT)
- *frames = MIN(*frames, EMPTY_FRAMES - current_level(iodev));
- else
- *frames = MIN(*frames, current_level(iodev));
+ if (iodev->direction == CRAS_STREAM_OUTPUT) {
+ avail = EMPTY_FRAMES - current_level(iodev);
+ *frames = MIN(*frames, avail);
+ } else {
+ current = current_level(iodev);
+ *frames = MIN(*frames, current);
+ }
iodev->area->frames = *frames;
cras_audio_area_config_buf_pointers(iodev->area, iodev->format,
@@ -163,13 +157,18 @@ static int put_buffer(struct cras_iodev *iodev, unsigned frames)
static int flush_buffer(struct cras_iodev *iodev)
{
struct empty_iodev *empty_iodev = (struct empty_iodev *)iodev;
+
empty_iodev->buffer_level = current_level(iodev);
- if (iodev->direction == CRAS_STREAM_INPUT)
+ if (iodev->direction == CRAS_STREAM_INPUT) {
empty_iodev->buffer_level = 0;
+ clock_gettime(CLOCK_MONOTONIC_RAW,
+ &empty_iodev->last_buffer_access);
+ }
return 0;
}
-static void update_active_node(struct cras_iodev *iodev, unsigned node_idx)
+static void update_active_node(struct cras_iodev *iodev, unsigned node_idx,
+ unsigned dev_enabled)
{
}
@@ -195,18 +194,17 @@ struct cras_iodev *empty_iodev_create(enum CRAS_STREAM_DIRECTION direction)
iodev->supported_rates = empty_supported_rates;
iodev->supported_channel_counts = empty_supported_channel_counts;
iodev->supported_formats = empty_supported_formats;
- iodev->buffer_size = EMPTY_BUFFER_SIZE;
+ iodev->buffer_size = EMPTY_FRAMES;
iodev->open_dev = open_dev;
iodev->close_dev = close_dev;
- iodev->is_open = is_open;
iodev->frames_queued = frames_queued;
iodev->delay_frames = delay_frames;
iodev->get_buffer = get_buffer;
iodev->put_buffer = put_buffer;
iodev->flush_buffer = flush_buffer;
- iodev->dev_running = dev_running;
iodev->update_active_node = update_active_node;
+ iodev->no_stream = cras_iodev_default_no_stream_playback;
/* Create a dummy ionode */
node = (struct cras_ionode *)calloc(1, sizeof(*node));
diff --git a/cras/src/server/cras_fmt_conv.c b/cras/src/server/cras_fmt_conv.c
index d1be3156..729b3477 100644
--- a/cras/src/server/cras_fmt_conv.c
+++ b/cras/src/server/cras_fmt_conv.c
@@ -610,6 +610,58 @@ void cras_fmt_conv_destroy(struct cras_fmt_conv *conv)
free(conv);
}
+struct cras_fmt_conv *cras_channel_remix_conv_create(
+ unsigned int num_channels,
+ const float *coefficient)
+{
+ struct cras_fmt_conv *conv;
+ unsigned out_ch, in_ch;
+
+ conv = calloc(1, sizeof(*conv));
+ if (conv == NULL)
+ return NULL;
+ conv->in_fmt.num_channels = num_channels;
+ conv->out_fmt.num_channels = num_channels;
+
+ conv->ch_conv_mtx = cras_channel_conv_matrix_alloc(num_channels,
+ num_channels);
+ /* Convert the coeffiencnt array to conversion matrix. */
+ for (out_ch = 0; out_ch < num_channels; out_ch++)
+ for (in_ch = 0; in_ch < num_channels; in_ch++)
+ conv->ch_conv_mtx[out_ch][in_ch] =
+ coefficient[in_ch + out_ch * num_channels];
+
+ conv->num_converters = 1;
+ conv->tmp_bufs[0] = malloc(4 * /* width in bytes largest format. */
+ num_channels);
+ return conv;
+}
+
+void cras_channel_remix_convert(struct cras_fmt_conv *conv,
+ const struct cras_audio_format *fmt,
+ uint8_t *in_buf,
+ size_t nframes)
+{
+ unsigned ch, fr;
+ int16_t *tmp = (int16_t *)conv->tmp_bufs[0];
+ int16_t *buf = (int16_t *)in_buf;
+
+ /* Do remix only when input buffer has the same number of channels. */
+ if (fmt->num_channels != conv->in_fmt.num_channels)
+ return;
+
+ for (fr = 0; fr < nframes; fr++) {
+ for (ch = 0; ch < conv->in_fmt.num_channels; ch++)
+ tmp[ch] = multiply_buf_with_coef(
+ conv->ch_conv_mtx[ch],
+ buf,
+ conv->in_fmt.num_channels);
+ for (ch = 0; ch < conv->in_fmt.num_channels; ch++)
+ buf[ch] = tmp[ch];
+ buf += conv->in_fmt.num_channels;
+ }
+}
+
const struct cras_audio_format *cras_fmt_conv_in_format(
const struct cras_fmt_conv *conv)
{
diff --git a/cras/src/server/cras_fmt_conv.h b/cras/src/server/cras_fmt_conv.h
index 518a7107..732a5176 100644
--- a/cras/src/server/cras_fmt_conv.h
+++ b/cras/src/server/cras_fmt_conv.h
@@ -25,6 +25,29 @@ struct cras_fmt_conv *cras_fmt_conv_create(const struct cras_audio_format *in,
size_t pre_linear_resample);
void cras_fmt_conv_destroy(struct cras_fmt_conv *conv);
+/* Creates the format converter for channel remixing. The conversion takes
+ * a N by N float matrix, to multiply each N-channels sample.
+ * Args:
+ * num_channels - Number of channels of PCM data.
+ * coefficient - Float array of length N * N representing the conversion
+ * matrix, where matrix[i][j] corresponds to coefficient[i * N + j]
+ */
+struct cras_fmt_conv *cras_channel_remix_conv_create(
+ unsigned int num_channels,
+ const float *coefficient);
+
+/* Converts nframes of sample from in_buf, using given remix converter.
+ * Args:
+ * conv - The format converter.
+ * fmt - The format of the buffer to convert.
+ * in_buf - The buffer to convert.
+ * nframes - The number of frames to convert.
+ */
+void cras_channel_remix_convert(struct cras_fmt_conv *conv,
+ const struct cras_audio_format *fmt,
+ uint8_t *in_buf,
+ size_t nframes);
+
/* Get the input format of the converter. */
const struct cras_audio_format *cras_fmt_conv_in_format(
const struct cras_fmt_conv *conv);
diff --git a/cras/src/server/cras_gpio_jack.c b/cras/src/server/cras_gpio_jack.c
index e8c5f514..29784c48 100644
--- a/cras/src/server/cras_gpio_jack.c
+++ b/cras/src/server/cras_gpio_jack.c
@@ -7,11 +7,11 @@
#include <linux/input.h>
#include <stdio.h>
#include <string.h>
+#include <syslog.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <libudev.h>
-#include <regex.h>
#include "cras_util.h"
#include "cras_gpio_jack.h"
@@ -41,31 +41,6 @@ int gpio_switch_eviocgsw(int fd, void *bits, size_t n_bytes)
return ioctl(fd, EVIOCGSW(n_bytes), bits);
}
-static void compile_regex(regex_t *regex, const char *str)
-{
- int r;
- r = regcomp(regex, str, REG_EXTENDED);
- assert(r == 0);
-}
-
-static int jack_matches_string(const char *jack, const char *re)
-{
- regmatch_t m[1];
- regex_t regex;
- unsigned success;
-
- compile_regex(&regex, re);
- success = regexec(&regex, jack, ARRAY_SIZE(m), m, 0) == 0;
- regfree(&regex);
- return success;
-}
-
-/* sys_input_get_device_name:
- *
- * Returns the heap-allocated device name of a /dev/input/event*
- * pathname. Caller is responsible for releasing.
- *
- */
char *sys_input_get_device_name(const char *path)
{
char name[256];
@@ -75,27 +50,22 @@ char *sys_input_get_device_name(const char *path)
gpio_switch_eviocgname(fd, name, sizeof(name));
close(fd);
return strdup(name);
- } else
+ } else {
+ syslog(LOG_WARNING, "Could not open '%s': %s",
+ path, strerror(errno));
return NULL;
+ }
}
-/* gpio_get_switch_names:
- *
- * Fills 'names' with up to 'n_names' entries of
- * '/dev/input/event*' pathnames which are associated with a GPIO
- * jack of the specified 'direction'.
- *
- * Returns the number of filenames found.
- *
- */
-unsigned gpio_get_switch_names(enum CRAS_STREAM_DIRECTION direction,
- char **names, size_t n_names)
+void gpio_switch_list_for_each(gpio_switch_list_callback callback, void *arg)
{
struct udev *udev;
struct udev_enumerate *enumerate;
struct udev_list_entry *dl;
struct udev_list_entry *dev_list_entry;
- unsigned n = 0;
+
+ if (!callback)
+ return;
udev = udev_new();
assert(udev != NULL);
@@ -110,7 +80,6 @@ unsigned gpio_get_switch_names(enum CRAS_STREAM_DIRECTION direction,
path);
const char *devnode = udev_device_get_devnode(dev);
char *ioctl_name;
- int save;
if (devnode == NULL)
continue;
@@ -119,22 +88,12 @@ unsigned gpio_get_switch_names(enum CRAS_STREAM_DIRECTION direction,
if (ioctl_name == NULL)
continue;
- save = ((direction == CRAS_STREAM_INPUT &&
- jack_matches_string(ioctl_name, "^.*Mic Jack$"))) ||
- (direction == CRAS_STREAM_OUTPUT &&
- jack_matches_string(ioctl_name,
- "^.*Headphone Jack$")) ||
- (jack_matches_string(ioctl_name,
- "^.*Headset Jack$")) ||
- (direction == CRAS_STREAM_OUTPUT &&
- jack_matches_string(ioctl_name, "^.*HDMI Jack$"));
-
- if (save && n < n_names)
- names[n++] = strdup(devnode);
-
+ if (callback(devnode, ioctl_name, arg)) {
+ free(ioctl_name);
+ break;
+ }
free(ioctl_name);
}
udev_enumerate_unref(enumerate);
udev_unref(udev);
- return n;
}
diff --git a/cras/src/server/cras_gpio_jack.h b/cras/src/server/cras_gpio_jack.h
index 0ac3ddb8..1c1a2a8d 100644
--- a/cras/src/server/cras_gpio_jack.h
+++ b/cras/src/server/cras_gpio_jack.h
@@ -8,13 +8,41 @@
#include "cras_types.h"
+struct mixer_name;
+
int gpio_switch_open(const char *pathname);
int gpio_switch_read(int fd, void *buf, size_t n_bytes);
int gpio_switch_eviocgbit(int fd, void *buf, size_t n_bytes);
int gpio_switch_eviocgsw(int fd, void *bits, size_t n_bytes);
-unsigned gpio_get_switch_names(enum CRAS_STREAM_DIRECTION direction,
- char **names, size_t n_names);
+/* sys_input_get_device_name:
+ *
+ * Returns the heap-allocated device name of a /dev/input/event*
+ * pathname. Caller is responsible for releasing.
+ */
char *sys_input_get_device_name(const char *path);
+
+/* List for each callback function.
+ *
+ * Args:
+ * dev_path - Full path to the GPIO device.
+ * dev_name - The name of the GPIO device.
+ * arg - The argument passed to gpio_switch_list_for_each.
+ *
+ * Returns:
+ * 0 to continue searching, non-zero otherwise.
+ */
+typedef int (*gpio_switch_list_callback)(const char *dev_path,
+ const char *dev_name,
+ void *arg);
+
+/* Execute the given callback on each GPIO device.
+ *
+ * Args:
+ * callback - The callback to execute.
+ * arg - An argument to pass to the callback.
+ */
+void gpio_switch_list_for_each(gpio_switch_list_callback callback, void *arg);
+
#endif
diff --git a/cras/src/server/cras_hfp_ag_profile.c b/cras/src/server/cras_hfp_ag_profile.c
index 9ad48413..29c3e0d9 100644
--- a/cras/src/server/cras_hfp_ag_profile.c
+++ b/cras/src/server/cras_hfp_ag_profile.c
@@ -17,7 +17,6 @@
#include "cras_hfp_iodev.h"
#include "cras_hfp_slc.h"
#include "cras_system_state.h"
-#include "cras_tm.h"
#include "utlist.h"
#define STR(s) #s
@@ -71,8 +70,6 @@
" </attribute>" \
"</record>"
-static const unsigned int A2DP_RETRY_DELAY_MS = 500;
-static const unsigned int A2DP_MAX_RETRIES = 10;
/* Object representing the audio gateway role for HFP/HSP.
* Members:
@@ -116,8 +113,6 @@ static void destroy_audio_gateway(struct audio_gateway *ag)
if (ag->slc_handle)
hfp_slc_destroy(ag->slc_handle);
- cras_bt_device_cancel_a2dp_delay_timer(ag->device);
-
/* If the bt device is not using a2dp, do a deeper clean up
* to force disconnect it. */
if (!cras_bt_device_has_a2dp(ag->device))
@@ -137,82 +132,22 @@ static int has_audio_gateway(struct cras_bt_device *device)
return 0;
}
-/* Creates the iodevs to start the audio gateway. */
-static int start_audio_gateway(struct audio_gateway *ag)
-{
- ag->info = hfp_info_create();
- ag->idev = hfp_iodev_create(CRAS_STREAM_INPUT, ag->device,
- ag->slc_handle,
- ag->profile, ag->info);
- ag->odev = hfp_iodev_create(CRAS_STREAM_OUTPUT, ag->device,
- ag->slc_handle,
- ag->profile, ag->info);
-
- if (!ag->idev && !ag->odev) {
- destroy_audio_gateway(ag);
- return -ENOMEM;
- }
-
- return 0;
-}
-
static void cras_hfp_ag_release(struct cras_bt_profile *profile)
{
cras_hfp_ag_suspend();
}
-/* Checks if a2dp connection is present. If not then delay the
- * start of audio gateway until the max number of retry is reached.
- */
-static void a2dp_delay_cb(struct cras_timer *timer, void *arg)
-{
- struct audio_gateway *ag = (struct audio_gateway *)arg;
- struct cras_tm *tm = cras_system_state_get_tm();
-
- cras_bt_device_rm_a2dp_delay_timer(ag->device);
-
- if (cras_bt_device_has_a2dp(ag->device))
- goto start_ag;
-
- if (--ag->a2dp_delay_retries == 0)
- goto start_ag;
-
- cras_bt_device_add_a2dp_delay_timer(
- ag->device,
- cras_tm_create_timer(tm, A2DP_RETRY_DELAY_MS,
- a2dp_delay_cb, ag));
- return;
-
-start_ag:
- if (start_audio_gateway(ag))
- syslog(LOG_ERR, "Start audio gateway failed");
-}
-
+/* Callback triggered when SLC is initialized. */
static int cras_hfp_ag_slc_initialized(struct hfp_slc_handle *handle)
{
struct audio_gateway *ag;
- struct cras_tm *tm = cras_system_state_get_tm();
- int rc;
DL_SEARCH_SCALAR(connected_ags, ag, slc_handle, handle);
if (!ag)
return -EINVAL;
- /* This is a HFP/HSP only headset. */
- if (!cras_bt_device_supports_profile(
- ag->device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK)) {
- rc = start_audio_gateway(ag);
- if (rc)
- syslog(LOG_ERR, "Start audio gateway failed");
- return rc;
- }
-
- ag->a2dp_delay_retries = A2DP_MAX_RETRIES;
- cras_bt_device_add_a2dp_delay_timer(
- ag->device,
- cras_tm_create_timer(tm, A2DP_RETRY_DELAY_MS,
- a2dp_delay_cb, ag));
- return 0;
+ /* Defer the starting of audio gateway to bt_device. */
+ return cras_bt_device_audio_gateway_initialized(ag->device);
}
static int cras_hfp_ag_slc_disconnected(struct hfp_slc_handle *handle)
@@ -270,7 +205,7 @@ static void possibly_remove_conflict_dev(void *data)
/* Kick out any previously connected a2dp iodev. */
a2dp_device = cras_a2dp_connected_device();
if (a2dp_device && a2dp_device != device) {
- cras_a2dp_suspend_connected_device();
+ cras_a2dp_suspend_connected_device(a2dp_device);
cras_bt_device_disconnect(new_ag->conn, a2dp_device);
}
}
@@ -299,6 +234,7 @@ static int cras_hfp_ag_new_connection(DBusConnection *conn,
ag->profile = cras_bt_device_profile_from_uuid(profile->uuid);
ag->slc_handle = hfp_slc_create(rfcomm_fd,
0,
+ device,
cras_hfp_ag_slc_initialized,
cras_hfp_ag_slc_disconnected);
DL_APPEND(connected_ags, ag);
@@ -360,7 +296,7 @@ static int cras_hsp_ag_new_connection(DBusConnection *conn,
ag->device = device;
ag->conn = conn;
ag->profile = cras_bt_device_profile_from_uuid(profile->uuid);
- ag->slc_handle = hfp_slc_create(rfcomm_fd, 1, NULL,
+ ag->slc_handle = hfp_slc_create(rfcomm_fd, 1, device, NULL,
cras_hfp_ag_slc_disconnected);
DL_APPEND(connected_ags, ag);
cras_hfp_ag_slc_initialized(ag->slc_handle);
@@ -380,6 +316,30 @@ static struct cras_bt_profile cras_hsp_ag_profile = {
.cancel = cras_hfp_ag_cancel
};
+int cras_hfp_ag_start(struct cras_bt_device *device)
+{
+ struct audio_gateway *ag;
+
+ DL_SEARCH_SCALAR(connected_ags, ag, device, device);
+ if (ag == NULL)
+ return -EEXIST;
+
+ ag->info = hfp_info_create();
+ ag->idev = hfp_iodev_create(CRAS_STREAM_INPUT, ag->device,
+ ag->slc_handle,
+ ag->profile, ag->info);
+ ag->odev = hfp_iodev_create(CRAS_STREAM_OUTPUT, ag->device,
+ ag->slc_handle,
+ ag->profile, ag->info);
+
+ if (!ag->idev && !ag->odev) {
+ destroy_audio_gateway(ag);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
void cras_hfp_ag_suspend()
{
struct audio_gateway *ag;
@@ -387,6 +347,15 @@ void cras_hfp_ag_suspend()
destroy_audio_gateway(ag);
}
+void cras_hfp_ag_suspend_connected_device(struct cras_bt_device *device)
+{
+ struct audio_gateway *ag;
+
+ DL_SEARCH_SCALAR(connected_ags, ag, device, device);
+ if (ag)
+ destroy_audio_gateway(ag);
+}
+
struct hfp_slc_handle *cras_hfp_ag_get_active_handle()
{
/* Returns the first handle for HFP qualification. In future we
diff --git a/cras/src/server/cras_hfp_ag_profile.h b/cras/src/server/cras_hfp_ag_profile.h
index 01fec691..bc3e0428 100644
--- a/cras/src/server/cras_hfp_ag_profile.h
+++ b/cras/src/server/cras_hfp_ag_profile.h
@@ -45,10 +45,16 @@ int cras_hfp_ag_profile_create(DBusConnection *conn);
/* Adds a profile instance for HSP AG (Headset Profile Audio Gateway). */
int cras_hsp_ag_profile_create(DBusConnection *conn);
+/* Starts the HFP audio gateway for audio input/output. */
+int cras_hfp_ag_start(struct cras_bt_device *device);
+
/* Suspends all connected audio gateways, used to stop HFP/HSP audio when
* an A2DP only device is connected. */
void cras_hfp_ag_suspend();
+/* Suspends audio gateway associated with given bt device. */
+void cras_hfp_ag_suspend_connected_device(struct cras_bt_device *device);
+
/* Gets the active SLC handle. Used for HFP qualification. */
struct hfp_slc_handle *cras_hfp_ag_get_active_handle();
diff --git a/cras/src/server/cras_hfp_info.c b/cras/src/server/cras_hfp_info.c
index 2bf1c1f4..d85d2852 100644
--- a/cras/src/server/cras_hfp_info.c
+++ b/cras/src/server/cras_hfp_info.c
@@ -11,6 +11,7 @@
#include "audio_thread.h"
#include "byte_buffer.h"
+#include "cras_iodev_list.h"
#include "cras_hfp_info.h"
#include "utlist.h"
@@ -353,7 +354,9 @@ int hfp_info_stop(struct hfp_info *info)
if (!info->started)
return 0;
- audio_thread_rm_callback(info->fd);
+ audio_thread_rm_callback_sync(
+ cras_iodev_list_get_audio_thread(),
+ info->fd);
close(info->fd);
info->fd = 0;
diff --git a/cras/src/server/cras_hfp_info.h b/cras/src/server/cras_hfp_info.h
index a6a48c7e..6d980a9e 100644
--- a/cras/src/server/cras_hfp_info.h
+++ b/cras/src/server/cras_hfp_info.h
@@ -47,10 +47,11 @@ int hfp_info_stop(struct hfp_info *info);
* Args:
* info - The hfp_info holding the buffer to query.
* dev - The iodev to indicate which buffer to query, playback
- * or capture, depend on its direction.
+ * or capture, depending on its direction.
*/
int hfp_buf_queued(struct hfp_info *info, const struct cras_iodev *dev);
+
/* Gets how many bytes of the buffer are used.
* Args:
* info - The hfp_info holding buffer.
diff --git a/cras/src/server/cras_hfp_iodev.c b/cras/src/server/cras_hfp_iodev.c
index 717f82f0..10314e63 100644
--- a/cras/src/server/cras_hfp_iodev.c
+++ b/cras/src/server/cras_hfp_iodev.c
@@ -22,7 +22,6 @@ struct hfp_io {
struct cras_bt_device *device;
struct hfp_slc_handle *slc;
struct hfp_info *info;
- int opened;
};
static int update_supported_formats(struct cras_iodev *iodev)
@@ -49,13 +48,17 @@ static int update_supported_formats(struct cras_iodev *iodev)
return 0;
}
-static int frames_queued(const struct cras_iodev *iodev)
+static int frames_queued(const struct cras_iodev *iodev,
+ struct timespec *tstamp)
{
struct hfp_io *hfpio = (struct hfp_io *)iodev;
if (!hfp_info_running(hfpio->info))
return -1;
+ /* Do not enable timestamp mechanism on HFP device because last time
+ * stamp might be a long time ago and it is not really useful. */
+ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
return hfp_buf_queued(hfpio->info, iodev);
}
@@ -65,7 +68,7 @@ static void hfp_packet_size_changed(void *data)
struct hfp_io *hfpio = (struct hfp_io *)data;
struct cras_iodev *iodev = &hfpio->base;
- if (!iodev->is_open(iodev))
+ if (!cras_iodev_is_open(iodev))
return;
iodev->buffer_size = hfp_buf_size(hfpio->info, iodev);
cras_bt_device_iodev_buffer_size_changed(hfpio->device);
@@ -101,7 +104,6 @@ add_dev:
hfp_set_call_status(hfpio->slc, 1);
iodev->buffer_size = hfp_buf_size(hfpio->info, iodev);
- hfpio->opened = 1;
return 0;
error:
@@ -113,7 +115,6 @@ static int close_dev(struct cras_iodev *iodev)
{
struct hfp_io *hfpio = (struct hfp_io *)iodev;
- hfpio->opened = 0;
hfp_info_rm_iodev(hfpio->info, iodev);
if (hfp_info_running(hfpio->info) && !hfp_info_has_iodev(hfpio->info)) {
hfp_info_stop(hfpio->info);
@@ -125,20 +126,23 @@ static int close_dev(struct cras_iodev *iodev)
return 0;
}
-static int is_open(const struct cras_iodev *iodev)
+static void set_hfp_volume(struct cras_iodev *iodev)
{
+ size_t volume;
struct hfp_io *hfpio = (struct hfp_io *)iodev;
- return hfpio->opened;
-}
-static int dev_running(const struct cras_iodev *iodev)
-{
- return iodev->is_open(iodev);
+ volume = cras_system_get_volume();
+ if (iodev->active_node)
+ volume = cras_iodev_adjust_node_volume(iodev->active_node, volume);
+
+ hfp_event_speaker_gain(hfpio->slc, volume);
}
static int delay_frames(const struct cras_iodev *iodev)
{
- return frames_queued(iodev);
+ struct timespec tstamp;
+
+ return frames_queued(iodev, &tstamp);
}
static int get_buffer(struct cras_iodev *iodev,
@@ -186,7 +190,8 @@ static int flush_buffer(struct cras_iodev *iodev)
return 0;
}
-static void update_active_node(struct cras_iodev *iodev, unsigned node_idx)
+static void update_active_node(struct cras_iodev *iodev, unsigned node_idx,
+ unsigned dev_enabled)
{
}
@@ -235,11 +240,10 @@ struct cras_iodev *hfp_iodev_create(
cras_bt_device_object_path(device),
strlen(cras_bt_device_object_path(device)),
strlen(cras_bt_device_object_path(device)));
+ iodev->info.stable_id_new = iodev->info.stable_id;
iodev->open_dev= open_dev;
- iodev->is_open = is_open;
iodev->frames_queued = frames_queued;
- iodev->dev_running = dev_running;
iodev->delay_frames = delay_frames;
iodev->get_buffer = get_buffer;
iodev->put_buffer = put_buffer;
@@ -247,7 +251,7 @@ struct cras_iodev *hfp_iodev_create(
iodev->close_dev = close_dev;
iodev->update_supported_formats = update_supported_formats;
iodev->update_active_node = update_active_node;
- iodev->software_volume_needed = 1;
+ iodev->set_volume = set_hfp_volume;
node = (struct cras_ionode *)calloc(1, sizeof(*node));
node->dev = iodev;
diff --git a/cras/src/server/cras_hfp_iodev.h b/cras/src/server/cras_hfp_iodev.h
index 70576418..3a073d00 100644
--- a/cras/src/server/cras_hfp_iodev.h
+++ b/cras/src/server/cras_hfp_iodev.h
@@ -6,7 +6,7 @@
#ifndef CRAS_HFP_IODEV_H_
#define CRAS_HFP_IODEV_H_
-#include "cras_bt_transport.h"
+#include "cras_bt_device.h"
#include "cras_hfp_info.h"
#include "cras_types.h"
diff --git a/cras/src/server/cras_hfp_slc.c b/cras/src/server/cras_hfp_slc.c
index 330df97d..cc51055a 100644
--- a/cras/src/server/cras_hfp_slc.c
+++ b/cras/src/server/cras_hfp_slc.c
@@ -6,6 +6,7 @@
#include <sys/socket.h>
#include <syslog.h>
+#include "cras_bt_device.h"
#include "cras_telephony.h"
#include "cras_hfp_ag_profile.h"
#include "cras_hfp_slc.h"
@@ -52,6 +53,7 @@
* callheld - Current callheld status of AG stored in SLC.
* ind_event_report - Activate status of indicator events reporting.
* telephony - A reference of current telephony handle.
+ * device - The associated bt device.
*/
struct hfp_slc_handle {
char buf[SLC_BUF_SIZE_BYTES];
@@ -69,6 +71,7 @@ struct hfp_slc_handle {
int service;
int callheld;
int ind_event_report;
+ struct cras_bt_device *device;
struct cras_telephony_handle *telephony;
};
@@ -418,10 +421,13 @@ static int signal_gain_setting(struct hfp_slc_handle *handle,
return -EINVAL;
}
- // TODO(hychao): set mic/speaker gain
- gain = atoi(&cmd[7]);
- syslog(LOG_ERR, "Reported gain level %d for %s", gain,
- cmd[5] == 'S' ? "speaker" : "microphone");
+ /* Map 0 to the smallest non-zero scale 6/100, and 15 to
+ * 100/100 full. */
+ if (cmd[5] == 'S') {
+ gain = atoi(&cmd[7]);
+ cras_bt_device_update_hardware_volume(handle->device,
+ (gain + 1) * 100 / 16);
+ }
return hfp_send(handle, "OK");
}
@@ -610,6 +616,7 @@ static void slc_watch_callback(void *arg)
struct hfp_slc_handle *hfp_slc_create(int fd,
int is_hsp,
+ struct cras_bt_device *device,
hfp_slc_init_cb init_cb,
hfp_slc_disconnect_cb disconnect_cb)
{
@@ -621,6 +628,7 @@ struct hfp_slc_handle *hfp_slc_create(int fd,
handle->rfcomm_fd = fd;
handle->is_hsp = is_hsp;
+ handle->device = device;
handle->init_cb = init_cb;
handle->disconnect_cb = disconnect_cb;
handle->cli_active = 0;
diff --git a/cras/src/server/cras_hfp_slc.h b/cras/src/server/cras_hfp_slc.h
index 65908f19..dd0d05fd 100644
--- a/cras/src/server/cras_hfp_slc.h
+++ b/cras/src/server/cras_hfp_slc.h
@@ -19,12 +19,15 @@ typedef int (*hfp_slc_disconnect_cb)(struct hfp_slc_handle *handle);
* Args:
* fd - the rfcomm fd used to initialize service level connection
* is_hsp - if the slc handle is created for headset profile
+ * device - The bt device associated with the created slc object
* init_cb - the callback function to be triggered when a service level
* connection is initialized.
* disconnect_cb - the callback function to be triggered when the service
* level connection is disconnected.
*/
-struct hfp_slc_handle *hfp_slc_create(int fd, int is_hsp, hfp_slc_init_cb init_cb,
+struct hfp_slc_handle *hfp_slc_create(int fd, int is_hsp,
+ struct cras_bt_device *device,
+ hfp_slc_init_cb init_cb,
hfp_slc_disconnect_cb disconnect_cb);
/* Destroys an hfp_slc_handle. */
diff --git a/cras/src/server/cras_iodev.c b/cras/src/server/cras_iodev.c
index ed2df84d..69e65dbf 100644
--- a/cras/src/server/cras_iodev.c
+++ b/cras/src/server/cras_iodev.c
@@ -10,13 +10,18 @@
#include <syslog.h>
#include <time.h>
+#include "audio_thread.h"
+#include "audio_thread_log.h"
#include "buffer_share.h"
#include "cras_audio_area.h"
+#include "cras_device_monitor.h"
#include "cras_dsp.h"
#include "cras_dsp_pipeline.h"
+#include "cras_fmt_conv.h"
#include "cras_iodev.h"
#include "cras_iodev_list.h"
#include "cras_mix.h"
+#include "cras_ramp.h"
#include "cras_rstream.h"
#include "cras_system_state.h"
#include "cras_util.h"
@@ -25,6 +30,10 @@
#include "rate_estimator.h"
#include "softvol_curve.h"
+static const float RAMP_UNMUTE_DURATION_SECS = 0.5;
+static const float RAMP_NEW_STREAM_DURATION_SECS = 0.01;
+static const float RAMP_MUTE_DURATION_SECS = 0.1;
+
static const struct timespec rate_estimation_window_sz = {
20, 0 /* 20 sec. */
};
@@ -32,6 +41,215 @@ static const double rate_estimation_smooth_factor = 0.9f;
static void cras_iodev_alloc_dsp(struct cras_iodev *iodev);
+static int default_no_stream_playback(struct cras_iodev *odev)
+{
+ int rc;
+ unsigned int hw_level, fr_to_write;
+ unsigned int target_hw_level = odev->min_cb_level * 2;
+ struct timespec hw_tstamp;
+
+ /* The default action for no stream playback is to fill zeros. */
+ rc = cras_iodev_frames_queued(odev, &hw_tstamp);
+ if (rc < 0)
+ return rc;
+ hw_level = rc;
+
+ ATLOG(atlog, AUDIO_THREAD_ODEV_DEFAULT_NO_STREAMS,
+ odev->info.idx, hw_level, target_hw_level);
+
+ fr_to_write = cras_iodev_buffer_avail(odev, hw_level);
+ if (hw_level <= target_hw_level) {
+ fr_to_write = MIN(target_hw_level - hw_level, fr_to_write);
+ return cras_iodev_fill_odev_zeros(odev, fr_to_write);
+ }
+ return 0;
+}
+
+static int cras_iodev_start(struct cras_iodev *iodev)
+{
+ int rc;
+ if (!cras_iodev_is_open(iodev))
+ return -EPERM;
+ if (!iodev->start) {
+ syslog(LOG_ERR,
+ "start called on device %s not supporting start ops",
+ iodev->info.name);
+ return -EINVAL;
+ }
+ rc = iodev->start(iodev);
+ if (rc)
+ return rc;
+ iodev->state = CRAS_IODEV_STATE_NORMAL_RUN;
+ return 0;
+}
+
+/* Gets the number of frames ready for this device to play.
+ * It is the minimum number of available samples in dev_streams.
+ */
+static unsigned int dev_playback_frames(struct cras_iodev* odev)
+{
+ struct dev_stream *curr;
+ int frames = 0;
+
+ DL_FOREACH(odev->streams, curr) {
+ int dev_frames;
+
+ /* If this is a single output dev stream, updates the latest
+ * number of frames for playback. */
+ if (dev_stream_attached_devs(curr) == 1)
+ dev_stream_update_frames(curr);
+
+ dev_frames = dev_stream_playback_frames(curr);
+ /* Do not handle stream error or end of draining in this
+ * function because they should be handled in write_streams. */
+ if (dev_frames < 0)
+ continue;
+ if (!dev_frames) {
+ if(cras_rstream_get_is_draining(curr->stream))
+ continue;
+ else
+ return 0;
+ }
+ if (frames == 0)
+ frames = dev_frames;
+ else
+ frames = MIN(dev_frames, frames);
+ }
+ return frames;
+}
+
+/* Let device enter/leave no stream playback.
+ * Args:
+ * iodev[in] - The output device.
+ * enable[in] - 1 to enter no stream playback, 0 to leave.
+ * Returns:
+ * 0 on success. Negative error code on failure.
+ */
+static int cras_iodev_no_stream_playback_transition(struct cras_iodev *odev,
+ int enable)
+{
+ int rc;
+
+ if (odev->direction != CRAS_STREAM_OUTPUT)
+ return -EINVAL;
+
+ /* This function is for transition between normal run and
+ * no stream run state.
+ */
+ if ((odev->state != CRAS_IODEV_STATE_NORMAL_RUN) &&
+ (odev->state != CRAS_IODEV_STATE_NO_STREAM_RUN))
+ return -EINVAL;
+
+ if (enable) {
+ ATLOG(atlog, AUDIO_THREAD_ODEV_NO_STREAMS,
+ odev->info.idx, 0, 0);
+ } else {
+ ATLOG(atlog, AUDIO_THREAD_ODEV_LEAVE_NO_STREAMS,
+ odev->info.idx, 0, 0);
+ }
+
+ rc = odev->no_stream(odev, enable);
+ if (rc < 0)
+ return rc;
+ if (enable)
+ odev->state = CRAS_IODEV_STATE_NO_STREAM_RUN;
+ else
+ odev->state = CRAS_IODEV_STATE_NORMAL_RUN;
+ return 0;
+}
+
+/* Determines if the output device should mute. It considers system mute,
+ * system volume, and active node volume on the device. */
+static int output_should_mute(struct cras_iodev *odev)
+{
+ /* System mute has highest priority. */
+ if (cras_system_get_mute())
+ return 1;
+
+ /* consider system volume and active node volume. */
+ return cras_iodev_is_zero_volume(odev);
+}
+
+int cras_iodev_is_zero_volume(const struct cras_iodev *odev)
+{
+ size_t system_volume;
+ unsigned int adjusted_node_volume;
+
+ system_volume = cras_system_get_volume();
+ if (odev->active_node) {
+ adjusted_node_volume = cras_iodev_adjust_node_volume(
+ odev->active_node, system_volume);
+ return (adjusted_node_volume == 0);
+ }
+ return (system_volume == 0);
+}
+
+/* Output device state transition diagram:
+ *
+ * ----------------
+ * -------------<-----------| S0 Closed |------<-------.
+ * | ---------------- |
+ * | | iodev_list enables |
+ * | | device and adds to |
+ * | V audio thread | iodev_list removes
+ * | ---------------- | device from
+ * | | S1 Open | | audio_thread and
+ * | ---------------- | closes device
+ * | Device with dummy start | |
+ * | ops transits into | Sample is ready |
+ * | no stream state right V |
+ * | after open. ---------------- |
+ * | | S2 Normal | |
+ * | ---------------- |
+ * | | ^ |
+ * | There is no stream | | Sample is ready |
+ * | V | |
+ * | ---------------- |
+ * ------------->-----------| S3 No Stream |------->------
+ * ----------------
+ *
+ * Device in open_devs can be in one of S1, S2, S3.
+ *
+ * cras_iodev_output_event_sample_ready change device state from S1 or S3 into
+ * S2.
+ */
+static int cras_iodev_output_event_sample_ready(struct cras_iodev *odev)
+{
+ if (odev->state == CRAS_IODEV_STATE_OPEN ||
+ odev->state == CRAS_IODEV_STATE_NO_STREAM_RUN) {
+ /* Starts ramping up if device should not be muted.
+ * Both mute and volume are taken into consideration.
+ */
+ if (odev->ramp && !output_should_mute(odev))
+ cras_iodev_start_ramp(
+ odev,
+ CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK);
+ }
+
+ if (odev->state == CRAS_IODEV_STATE_OPEN) {
+ /* S1 => S2:
+ * If device is not started yet, and there is sample ready from
+ * stream, fill 1 min_cb_level of zeros first and fill sample
+ * from stream later.
+ * Starts the device here to finish state transition. */
+ cras_iodev_fill_odev_zeros(odev, odev->min_cb_level);
+ ATLOG(atlog, AUDIO_THREAD_ODEV_START,
+ odev->info.idx, odev->min_cb_level, 0);
+ return cras_iodev_start(odev);
+ } else if (odev->state == CRAS_IODEV_STATE_NO_STREAM_RUN) {
+ /* S3 => S2:
+ * Device in no stream state get sample ready. Leave no stream
+ * state and transit to normal run state.*/
+ return cras_iodev_no_stream_playback_transition(odev, 0);
+ } else {
+ syslog(LOG_ERR,
+ "Device %s in state %d received sample ready event",
+ odev->info.name, odev->state);
+ return -EINVAL;
+ }
+ return 0;
+}
+
/*
* Exported Interface.
*/
@@ -106,10 +324,8 @@ static snd_pcm_format_t get_best_pcm_format(struct cras_iodev *iodev,
return iodev->supported_formats[0];
}
-/* Set default channel count and layout to an iodev.
- * iodev->format->num_channels is from get_best_channel_count.
- */
-static void set_default_channel_count_layout(struct cras_iodev *iodev)
+/* Set default channel layout to an iodev. */
+static void set_default_channel_layout(struct cras_iodev *iodev)
{
int8_t default_layout[CRAS_CH_MAX];
size_t i;
@@ -117,7 +333,6 @@ static void set_default_channel_count_layout(struct cras_iodev *iodev)
for (i = 0; i < CRAS_CH_MAX; i++)
default_layout[i] = i < iodev->format->num_channels ? i : -1;
- iodev->ext_format->num_channels = iodev->format->num_channels;
cras_audio_format_set_channel_layout(iodev->format, default_layout);
cras_audio_format_set_channel_layout(iodev->ext_format, default_layout);
}
@@ -151,9 +366,9 @@ static void cras_iodev_free_dsp(struct cras_iodev *iodev)
}
}
-/* Modifies the format to the one that will be presented to the device after
- * any format changes from the DSP. */
-static inline void adjust_dev_fmt_for_dsp(const struct cras_iodev *iodev)
+/* Modifies the number of channels in device format to the one that will be
+ * presented to the device after any channel changes from the DSP. */
+static inline void adjust_dev_channel_for_dsp(const struct cras_iodev *iodev)
{
struct cras_dsp_context *ctx = iodev->dsp_context;
@@ -183,12 +398,25 @@ static inline void adjust_dev_fmt_for_dsp(const struct cras_iodev *iodev)
static void update_channel_layout(struct cras_iodev *iodev)
{
int rc;
+
+ /*
+ * Output devices like internal speakers and headphones are 2-channel
+ * and do not need to update channel layout.
+ * For HDMI and USB devices that might have more than 2 channels, update
+ * channel layout only if more than 2 channel is requested.
+ */
+ if (iodev->direction == CRAS_STREAM_OUTPUT &&
+ iodev->format->num_channels <= 2) {
+ set_default_channel_layout(iodev);
+ return;
+ }
+
if (iodev->update_channel_layout == NULL)
return;
rc = iodev->update_channel_layout(iodev);
if (rc < 0) {
- set_default_channel_count_layout(iodev);
+ set_default_channel_layout(iodev);
} else {
cras_audio_format_set_channel_layout(
iodev->ext_format,
@@ -221,11 +449,17 @@ int cras_iodev_set_format(struct cras_iodev *iodev,
}
}
+ /* Finds the actual rate of device before allocating DSP
+ * because DSP needs to use the rate of device, not rate of
+ * stream. */
+ actual_rate = get_best_rate(iodev, fmt->frame_rate);
+ iodev->format->frame_rate = actual_rate;
+ iodev->ext_format->frame_rate = actual_rate;
+
cras_iodev_alloc_dsp(iodev);
if (iodev->dsp_context)
- adjust_dev_fmt_for_dsp(iodev);
+ adjust_dev_channel_for_dsp(iodev);
- actual_rate = get_best_rate(iodev, fmt->frame_rate);
actual_num_channels = get_best_channel_count(iodev,
iodev->format->num_channels);
actual_format = get_best_pcm_format(iodev, fmt->format);
@@ -235,8 +469,6 @@ int cras_iodev_set_format(struct cras_iodev *iodev,
rc = -EINVAL;
goto error;
}
- iodev->format->frame_rate = actual_rate;
- iodev->ext_format->frame_rate = actual_rate;
iodev->format->format = actual_format;
iodev->ext_format->format = actual_format;
if (iodev->format->num_channels != actual_num_channels) {
@@ -269,14 +501,42 @@ error:
void cras_iodev_update_dsp(struct cras_iodev *iodev)
{
+ char swap_lr_disabled = 1;
+
if (!iodev->dsp_context)
return;
- cras_dsp_set_variable(iodev->dsp_context, "dsp_name",
- iodev->dsp_name ? : "");
+ cras_dsp_set_variable_string(iodev->dsp_context, "dsp_name",
+ iodev->dsp_name ? : "");
+
+ if (iodev->active_node && iodev->active_node->left_right_swapped)
+ swap_lr_disabled = 0;
+
+ cras_dsp_set_variable_boolean(iodev->dsp_context, "swap_lr_disabled",
+ swap_lr_disabled);
+
cras_dsp_load_pipeline(iodev->dsp_context);
}
+
+int cras_iodev_dsp_set_swap_mode_for_node(struct cras_iodev *iodev,
+ struct cras_ionode *node, int enable)
+{
+ if (node->left_right_swapped == enable)
+ return 0;
+
+ /* Sets left_right_swapped property on the node. It will be used
+ * when cras_iodev_update_dsp is called. */
+ node->left_right_swapped = enable;
+
+ /* Possibly updates dsp if the node is active on the device and there
+ * is dsp context. If dsp context is not created yet,
+ * cras_iodev_update_dsp returns right away. */
+ if (iodev->active_node == node)
+ cras_iodev_update_dsp(iodev);
+ return 0;
+}
+
void cras_iodev_free_format(struct cras_iodev *iodev)
{
free(iodev->format);
@@ -309,6 +569,8 @@ void cras_iodev_free_resources(struct cras_iodev *iodev)
{
cras_iodev_free_dsp(iodev);
rate_estimator_destroy(iodev->rate_est);
+ if (iodev->ramp)
+ cras_ramp_destroy(iodev->ramp);
}
static void cras_iodev_alloc_dsp(struct cras_iodev *iodev)
@@ -321,7 +583,7 @@ static void cras_iodev_alloc_dsp(struct cras_iodev *iodev)
purpose = "capture";
cras_iodev_free_dsp(iodev);
- iodev->dsp_context = cras_dsp_context_new(iodev->ext_format->frame_rate,
+ iodev->dsp_context = cras_dsp_context_new(iodev->format->frame_rate,
purpose);
cras_iodev_update_dsp(iodev);
}
@@ -351,7 +613,7 @@ static void plug_node(struct cras_ionode *node, int plugged)
node->plugged = plugged;
if (plugged) {
gettimeofday(&node->plugged_time, NULL);
- } else {
+ } else if (node == node->dev->active_node) {
cras_iodev_list_disable_dev(node->dev);
}
cras_iodev_list_notify_nodes_changed();
@@ -445,7 +707,7 @@ void cras_iodev_set_active_node(struct cras_iodev *iodev,
struct cras_ionode *node)
{
iodev->active_node = node;
- cras_iodev_list_notify_active_node_changed();
+ cras_iodev_list_notify_active_node_changed(iodev->direction);
}
float cras_iodev_get_software_volume_scaler(struct cras_iodev *iodev)
@@ -460,6 +722,16 @@ float cras_iodev_get_software_volume_scaler(struct cras_iodev *iodev)
return softvol_get_scaler(volume);
}
+float cras_iodev_get_software_gain_scaler(const struct cras_iodev *iodev) {
+ float scaler = 1.0f;
+ if (cras_iodev_software_volume_needed(iodev)) {
+ long gain = cras_iodev_adjust_active_node_gain(
+ iodev, cras_system_get_capture_gain());
+ scaler = convert_softvol_scaler_from_dB(gain);
+ }
+ return scaler;
+}
+
int cras_iodev_add_stream(struct cras_iodev *iodev,
struct dev_stream *stream)
{
@@ -502,6 +774,12 @@ struct dev_stream *cras_iodev_rm_stream(struct cras_iodev *iodev,
buffer_share_destroy(iodev->buf_state);
iodev->buf_state = NULL;
iodev->min_cb_level = old_min_cb_level;
+ /* Let output device transit into no stream state if it's
+ * in normal run state now. Leave input device in normal
+ * run state. */
+ if ((iodev->direction == CRAS_STREAM_OUTPUT) &&
+ (iodev->state == CRAS_IODEV_STATE_NORMAL_RUN))
+ cras_iodev_no_stream_playback_transition(iodev, 1);
}
return ret;
}
@@ -554,15 +832,44 @@ int cras_iodev_open(struct cras_iodev *iodev, unsigned int cb_level)
iodev->min_cb_level = MIN(iodev->buffer_size / 2, cb_level);
iodev->max_cb_level = 0;
+ iodev->reset_request_pending = 0;
+ iodev->state = CRAS_IODEV_STATE_OPEN;
+
+ if (iodev->direction == CRAS_STREAM_OUTPUT) {
+ /* If device supports start ops, device can be in open state.
+ * Otherwise, device starts running right after opening. */
+ if (iodev->start)
+ iodev->state = CRAS_IODEV_STATE_OPEN;
+ else
+ iodev->state = CRAS_IODEV_STATE_NO_STREAM_RUN;
+ } else {
+ /* Input device starts running right after opening.
+ * No stream state is only for output device. Input device
+ * should be in normal run state. */
+ iodev->state = CRAS_IODEV_STATE_NORMAL_RUN;
+ }
+
return 0;
}
+enum CRAS_IODEV_STATE cras_iodev_state(const struct cras_iodev *iodev)
+{
+ return iodev->state;
+}
+
int cras_iodev_close(struct cras_iodev *iodev)
{
- if (!iodev->is_open(iodev))
+ int rc;
+ if (!cras_iodev_is_open(iodev))
return 0;
- return iodev->close_dev(iodev);
+ rc = iodev->close_dev(iodev);
+ if (rc)
+ return rc;
+ iodev->state = CRAS_IODEV_STATE_CLOSE;
+ if (iodev->ramp)
+ cras_ramp_reset(iodev->ramp);
+ return 0;
}
int cras_iodev_put_input_buffer(struct cras_iodev *iodev, unsigned int nframes)
@@ -575,12 +882,28 @@ int cras_iodev_put_output_buffer(struct cras_iodev *iodev, uint8_t *frames,
unsigned int nframes)
{
const struct cras_audio_format *fmt = iodev->format;
+ struct cras_fmt_conv * remix_converter =
+ audio_thread_get_global_remix_converter();
+ struct cras_ramp_action ramp_action = {
+ .type = CRAS_RAMP_ACTION_NONE,
+ .scaler = 0.0f,
+ .increment = 0.0f,
+ };
+ float software_volume_scaler;
+ int software_volume_needed = cras_iodev_software_volume_needed(iodev);
if (iodev->pre_dsp_hook)
iodev->pre_dsp_hook(frames, nframes, iodev->ext_format,
iodev->pre_dsp_hook_cb_data);
- if (cras_system_get_mute()) {
+ if (iodev->ramp) {
+ ramp_action = cras_ramp_get_current_action(iodev->ramp);
+ }
+
+ /* Mute samples if adjusted volume is 0 or system is muted, plus
+ * that this device is not ramping. */
+ if (output_should_mute(iodev) &&
+ ramp_action.type != CRAS_RAMP_ACTION_PARTIAL) {
const unsigned int frame_bytes = cras_get_format_bytes(fmt);
cras_mix_mute_buffer(frames, frame_bytes, nframes);
} else {
@@ -590,16 +913,42 @@ int cras_iodev_put_output_buffer(struct cras_iodev *iodev, uint8_t *frames,
iodev->post_dsp_hook(frames, nframes, fmt,
iodev->post_dsp_hook_cb_data);
- if (cras_iodev_software_volume_needed(iodev)) {
- unsigned int nsamples = nframes * fmt->num_channels;
- float scaler =
+ /* Compute scaler for software volume if needed. */
+ if (software_volume_needed) {
+ software_volume_scaler =
cras_iodev_get_software_volume_scaler(iodev);
+ }
+ if (ramp_action.type == CRAS_RAMP_ACTION_PARTIAL) {
+ /* Scale with increment for ramp and possibly
+ * software volume using cras_scale_buffer_increment.*/
+ float starting_scaler = ramp_action.scaler;
+ float increment = ramp_action.increment;
+
+ if (software_volume_needed) {
+ starting_scaler *= software_volume_scaler;
+ increment *= software_volume_scaler;
+ }
+
+ cras_scale_buffer_increment(
+ fmt->format, frames, nframes,
+ starting_scaler, increment,
+ fmt->num_channels);
+ cras_ramp_update_ramped_frames(iodev->ramp, nframes);
+ } else if (software_volume_needed) {
+ /* Just scale for software volume using
+ * cras_scale_buffer. */
+ unsigned int nsamples = nframes * fmt->num_channels;
cras_scale_buffer(fmt->format, frames,
- nsamples, scaler);
+ nsamples, software_volume_scaler);
}
}
+ if (remix_converter)
+ cras_channel_remix_convert(remix_converter,
+ iodev->format,
+ frames,
+ nframes);
rate_estimator_add_frames(iodev->rate_est, nframes);
return iodev->put_buffer(iodev, nframes);
}
@@ -612,10 +961,17 @@ int cras_iodev_get_input_buffer(struct cras_iodev *iodev,
const unsigned int frame_bytes = cras_get_format_bytes(fmt);
uint8_t *hw_buffer;
int rc;
+ unsigned frame_requested = *frames;
rc = iodev->get_buffer(iodev, area, frames);
if (rc < 0 || *frames == 0)
return rc;
+ if (*frames > frame_requested) {
+ syslog(LOG_ERR,
+ "frames returned from get_buffer is greater than "
+ "requested: %u > %u", *frames, frame_requested);
+ return -EINVAL;
+ }
/* TODO(dgreid) - This assumes interleaved audio. */
hw_buffer = (*area)->channels[0].buf;
@@ -632,15 +988,23 @@ int cras_iodev_get_output_buffer(struct cras_iodev *iodev,
struct cras_audio_area **area,
unsigned *frames)
{
- return iodev->get_buffer(iodev, area, frames);
+ int rc;
+ unsigned frame_requested = *frames;
+
+ rc = iodev->get_buffer(iodev, area, frames);
+ if (*frames > frame_requested) {
+ syslog(LOG_ERR,
+ "frames returned from get_buffer is greater than "
+ "requested: %u > %u", *frames, frame_requested);
+ return -EINVAL;
+ }
+ return rc;
}
-int cras_iodev_update_rate(struct cras_iodev *iodev, unsigned int level)
+int cras_iodev_update_rate(struct cras_iodev *iodev, unsigned int level,
+ struct timespec *level_tstamp)
{
- struct timespec now;
-
- clock_gettime(CLOCK_MONOTONIC_RAW, &now);
- return rate_estimator_check(iodev->rate_est, level, &now);
+ return rate_estimator_check(iodev->rate_est, level, level_tstamp);
}
int cras_iodev_reset_rate_estimator(const struct cras_iodev *iodev)
@@ -676,11 +1040,12 @@ int cras_iodev_get_dsp_delay(const struct cras_iodev *iodev)
return delay;
}
-int cras_iodev_frames_queued(struct cras_iodev *iodev)
+int cras_iodev_frames_queued(struct cras_iodev *iodev,
+ struct timespec *hw_tstamp)
{
int rc;
- rc = iodev->frames_queued(iodev);
+ rc = iodev->frames_queued(iodev, hw_tstamp);
if (rc < 0 || iodev->direction == CRAS_STREAM_INPUT)
return rc;
@@ -716,3 +1081,208 @@ void cras_iodev_register_post_dsp_hook(struct cras_iodev *iodev,
iodev->post_dsp_hook = loop_cb;
iodev->post_dsp_hook_cb_data = cb_data;
}
+
+int cras_iodev_fill_odev_zeros(struct cras_iodev *odev, unsigned int frames)
+{
+ struct cras_audio_area *area = NULL;
+ unsigned int frame_bytes, frames_written;
+ int rc;
+ uint8_t *buf;
+
+ if (odev->direction != CRAS_STREAM_OUTPUT)
+ return -EINVAL;
+
+ ATLOG(atlog, AUDIO_THREAD_FILL_ODEV_ZEROS, odev->info.idx, frames, 0);
+
+ frame_bytes = cras_get_format_bytes(odev->ext_format);
+ while (frames > 0) {
+ frames_written = frames;
+ rc = cras_iodev_get_output_buffer(odev, &area, &frames_written);
+ if (rc < 0) {
+ syslog(LOG_ERR, "fill zeros fail: %d", rc);
+ return rc;
+ }
+
+ /* This assumes consecutive channel areas. */
+ buf = area->channels[0].buf;
+ memset(buf, 0, frames_written * frame_bytes);
+ cras_iodev_put_output_buffer(odev, buf, frames_written);
+ frames -= frames_written;
+ }
+
+ return 0;
+}
+
+int cras_iodev_output_underrun(struct cras_iodev *odev) {
+ if (odev->output_underrun)
+ return odev->output_underrun(odev);
+ else
+ return cras_iodev_fill_odev_zeros(odev, odev->min_cb_level);
+}
+
+int cras_iodev_odev_should_wake(const struct cras_iodev *odev)
+{
+ if (odev->direction != CRAS_STREAM_OUTPUT)
+ return 0;
+
+ if (odev->output_should_wake)
+ return odev->output_should_wake(odev);
+
+ /* Do not wake up for device not started yet. */
+ return (odev->state == CRAS_IODEV_STATE_NORMAL_RUN ||
+ odev->state == CRAS_IODEV_STATE_NO_STREAM_RUN);
+}
+
+unsigned int cras_iodev_frames_to_play_in_sleep(struct cras_iodev *odev,
+ unsigned int *hw_level,
+ struct timespec *hw_tstamp)
+{
+ int rc;
+
+ rc = cras_iodev_frames_queued(odev, hw_tstamp);
+ *hw_level = (rc < 0) ? 0 : rc;
+
+ if (odev->streams) {
+ /* Schedule that audio thread will wake up when
+ * hw_level drops to 0.
+ * This should not cause underrun because audio thread
+ * should be waken up by the reply from client. */
+ return *hw_level;
+ }
+ /* When this device has no stream, schedule audio thread to wake up
+ * when hw_level drops to min_cb_level so audio thread can fill
+ * zeros to it. */
+ if (*hw_level > odev->min_cb_level)
+ return *hw_level - odev->min_cb_level;
+ else
+ return 0;
+}
+
+int cras_iodev_default_no_stream_playback(struct cras_iodev *odev, int enable)
+{
+ if (enable)
+ return default_no_stream_playback(odev);
+ return 0;
+}
+
+int cras_iodev_prepare_output_before_write_samples(struct cras_iodev *odev)
+{
+ int may_enter_normal_run;
+ enum CRAS_IODEV_STATE state;
+
+ if (odev->direction != CRAS_STREAM_OUTPUT)
+ return -EINVAL;
+
+ state = cras_iodev_state(odev);
+
+ may_enter_normal_run = (state == CRAS_IODEV_STATE_OPEN ||
+ state == CRAS_IODEV_STATE_NO_STREAM_RUN);
+
+ if (may_enter_normal_run && dev_playback_frames(odev))
+ return cras_iodev_output_event_sample_ready(odev);
+
+ /* no_stream ops is called every cycle in no_stream state. */
+ if (state == CRAS_IODEV_STATE_NO_STREAM_RUN)
+ return odev->no_stream(odev, 1);
+
+ return 0;
+}
+
+unsigned int cras_iodev_get_num_underruns(const struct cras_iodev *iodev)
+{
+ if (iodev->get_num_underruns)
+ return iodev->get_num_underruns(iodev);
+ return 0;
+}
+
+unsigned int cras_iodev_get_num_severe_underruns(const struct cras_iodev *iodev)
+{
+ if (iodev->get_num_severe_underruns)
+ return iodev->get_num_severe_underruns(iodev);
+ return 0;
+}
+
+int cras_iodev_reset_request(struct cras_iodev* iodev)
+{
+ /* Ignore requests if there is a pending request.
+ * This function sends the request from audio thread to main
+ * thread when audio thread finds a device is in a bad state
+ * e.g. severe underrun. Before main thread receives the
+ * request and resets device, audio thread might try to send
+ * multiple requests because it finds device is still in bad
+ * state. We should ignore requests in this cause. Otherwise,
+ * main thread will reset device multiple times.
+ * The flag is cleared in cras_iodev_open.
+ * */
+ if (iodev->reset_request_pending)
+ return 0;
+ iodev->reset_request_pending = 1;
+ return cras_device_monitor_reset_device(iodev);
+}
+
+static void ramp_mute_callback(void *data)
+{
+ struct cras_iodev *odev = (struct cras_iodev *)data;
+ cras_device_monitor_set_device_mute_state(odev);
+}
+
+/* Used in audio thread. Check the docstrings of CRAS_IODEV_RAMP_REQUEST. */
+int cras_iodev_start_ramp(struct cras_iodev *odev,
+ enum CRAS_IODEV_RAMP_REQUEST request)
+{
+ cras_ramp_cb cb = NULL;
+ void *cb_data = NULL;
+ int rc, up;
+ float duration_secs;
+
+ /* Ignores request if device is closed. */
+ if (!cras_iodev_is_open(odev))
+ return 0;
+
+ switch (request) {
+ case CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE:
+ up = 1;
+ duration_secs = RAMP_UNMUTE_DURATION_SECS;
+ break;
+ case CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK:
+ up = 1;
+ duration_secs = RAMP_NEW_STREAM_DURATION_SECS;
+ break;
+ /* Unmute -> mute. Callback to set mute state should be called after
+ * ramping is done. */
+ case CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE:
+ up = 0;
+ duration_secs = RAMP_MUTE_DURATION_SECS;
+ cb = ramp_mute_callback;
+ cb_data = (void*)odev;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Starts ramping. */
+ rc = cras_ramp_start(
+ odev->ramp, up,
+ duration_secs * odev->format->frame_rate,
+ cb, cb_data);
+
+ if (rc)
+ return rc;
+
+ /* Mute -> unmute case, unmute state should be set after ramping is
+ * started so device can start playing with samples close to 0. */
+ if (request == CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE)
+ cras_device_monitor_set_device_mute_state(odev);
+
+ return 0;
+}
+
+int cras_iodev_set_mute(struct cras_iodev* iodev)
+{
+ if (!cras_iodev_is_open(iodev))
+ return 0;
+
+ if (iodev->set_mute)
+ iodev->set_mute(iodev);
+ return 0;
+}
diff --git a/cras/src/server/cras_iodev.h b/cras/src/server/cras_iodev.h
index d5ccb36f..51a00038 100644
--- a/cras/src/server/cras_iodev.h
+++ b/cras/src/server/cras_iodev.h
@@ -19,6 +19,7 @@
#include "cras_messages.h"
struct buffer_share;
+struct cras_ramp;
struct cras_rstream;
struct cras_audio_area;
struct cras_audio_format;
@@ -33,6 +34,17 @@ typedef int (*loopback_hook_t)(const uint8_t *frames, unsigned int nframes,
const struct cras_audio_format *fmt,
void *cb_data);
+/* State of an iodev.
+ * no_stream state is only supported on output device.
+ * Open state is only supported for device supporting start ops.
+ */
+enum CRAS_IODEV_STATE {
+ CRAS_IODEV_STATE_CLOSE = 0,
+ CRAS_IODEV_STATE_OPEN = 1,
+ CRAS_IODEV_STATE_NORMAL_RUN = 2,
+ CRAS_IODEV_STATE_NO_STREAM_RUN = 3,
+};
+
/* Holds an output/input node for this device. An ionode is a control that
* can be switched on and off such as headphones or speakers.
* Members:
@@ -44,13 +56,20 @@ typedef int (*loopback_hook_t)(const uint8_t *frames, unsigned int nframes,
* capture_gain - per-node capture gain/attenuation (in 100*dBFS)
* 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.
+ * active_hotword_model - name of the currently selected hotword model.
* softvol_scalers - pointer to software volume scalers.
- * software_volume_needed - True if the volume range of the node is
- * smaller than desired.
+ * software_volume_needed - For output: True if the volume range of the node
+ * is smaller than desired. For input: True if this node needs software
+ * gain.
+ * max_software_gain - The maximum software gain in dBm if needed.
+ * stable_id - id for node that doesn't change after unplug/plug.
+ * stable_id_new - New stable_id, it will be deprecated and be put on
+ * stable_id.
*/
struct cras_ionode {
struct cras_iodev *dev;
@@ -61,10 +80,15 @@ struct cras_ionode {
long capture_gain;
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];
+ char active_hotword_model[CRAS_NODE_HOTWORD_MODEL_BUFFER_SIZE];
float *softvol_scalers;
int software_volume_needed;
+ long max_software_gain;
+ unsigned int stable_id;
+ unsigned int stable_id_new;
struct cras_ionode *prev, *next;
};
@@ -76,18 +100,40 @@ struct cras_ionode {
* set_swap_mode_for_node - Function to call to set swap mode for the node.
* open_dev - Opens the device.
* close_dev - Closes the device if it is open.
- * is_open - Checks if the device has been openned.
* update_supported_formats - Refresh supported frame rates and channel counts.
- * frames_queued - The number of frames in the audio buffer.
+ * frames_queued - The number of frames in the audio buffer, and fills tstamp
+ * with the associated timestamp. The timestamp is {0, 0} when
+ * the device hasn't started processing data (and on error).
* delay_frames - The delay of the next sample in frames.
* get_buffer - Returns a buffer to read/write to/from.
* put_buffer - Marks a buffer from get_buffer as read/written.
* flush_buffer - Flushes the buffer and return the number of frames flushed.
- * dev_running - Checks if the device is playing or recording, return 1 if it's
- * running, return 0 if not.
- * update_active_node - Update the active node using the selected/plugged state.
+ * start - Starts running device. This is optionally supported on output device.
+ * If device supports this ops, device can be in CRAS_IODEV_STATE_OPEN
+ * state after being opened.
+ * If device does not support this ops, then device will be in
+ * CRAS_IODEV_STATE_NO_STREAM_RUN.
+ * no_stream - (Optional) When there is no stream, we let device keep running
+ * for some time to save the time to open device for the next
+ * stream. This is the no stream state of an output device.
+ * The default action of no stream state is to fill zeros
+ * periodically. Device can implement this function to define
+ * its own optimization of entering/exiting no stream state.
+ * output_should_wake - (Optional) Checks if audio thread should schedule a
+ * wake for this output device. The default condition is
+ * whether the device is running. Device can implement this
+ * function to use its own condition.
+ * output_underrun - (Optional) Handle output device underrun.
+ * update_active_node - Update the active node when the selected device/node has
+ * changed.
* update_channel_layout - Update the channel layout base on set iodev->format,
* expect the best available layout be filled to iodev->format.
+ * set_hotword_model - Sets the hotword model to this iodev.
+ * get_hotword_models - Gets a comma separated string of the list of supported
+ * hotword models of this iodev.
+ * get_num_underruns - Gets number of underrun recorded so far.
+ * get_num_severe_underruns - Gets number of severe underrun recorded since
+ * iodev was created.
* format - The audio format being rendered or captured to hardware.
* ext_format - The audio format that is visible to the rest of the system.
* This can be different than the hardware if the device dsp changes it.
@@ -107,6 +153,8 @@ struct cras_ionode {
* is_enabled - True if this iodev is enabled, false otherwise.
* software_volume_needed - True if volume control is not supported by hardware.
* streams - List of audio streams serviced by dev.
+ * state - Device is in one of close, open, normal, or no_stream state defined
+ * in enum CRAS_IODEV_STATE.
* min_cb_level - min callback level of any stream attached.
* max_cb_level - max callback level of any stream attached.
* buf_state - If multiple streams are writing to this device, then this
@@ -118,6 +166,9 @@ struct cras_ionode {
* reference.
* pre_dsp_hook_cb_data - Callback data that will be passing to pre_dsp_hook.
* post_dsp_hook_cb_data - Callback data that will be passing to post_dsp_hook.
+ * reset_request_pending - The flag for pending reset request.
+ * ramp - The cras_ramp struct to control ramping up/down at mute/unmute and
+ * start of playback.
*/
struct cras_iodev {
void (*set_volume)(struct cras_iodev *iodev);
@@ -129,18 +180,27 @@ struct cras_iodev {
int enable);
int (*open_dev)(struct cras_iodev *iodev);
int (*close_dev)(struct cras_iodev *iodev);
- int (*is_open)(const struct cras_iodev *iodev);
int (*update_supported_formats)(struct cras_iodev *iodev);
- int (*frames_queued)(const struct cras_iodev *iodev);
+ int (*frames_queued)(const struct cras_iodev *iodev,
+ struct timespec *tstamp);
int (*delay_frames)(const struct cras_iodev *iodev);
int (*get_buffer)(struct cras_iodev *iodev,
struct cras_audio_area **area,
unsigned *frames);
int (*put_buffer)(struct cras_iodev *iodev, unsigned nwritten);
int (*flush_buffer)(struct cras_iodev *iodev);
- int (*dev_running)(const struct cras_iodev *iodev);
- void (*update_active_node)(struct cras_iodev *iodev, unsigned node_idx);
+ int (*start)(const struct cras_iodev *iodev);
+ int (*output_should_wake)(const struct cras_iodev *iodev);
+ int (*output_underrun)(struct cras_iodev *iodev);
+ int (*no_stream)(struct cras_iodev *iodev, int enable);
+ void (*update_active_node)(struct cras_iodev *iodev,
+ unsigned node_idx, unsigned dev_enabled);
int (*update_channel_layout)(struct cras_iodev *iodev);
+ int (*set_hotword_model)(struct cras_iodev *iodev,
+ const char *model_name);
+ char *(*get_hotword_models)(struct cras_iodev *iodev);
+ unsigned int (*get_num_underruns)(const struct cras_iodev *iodev);
+ unsigned int (*get_num_severe_underruns)(const struct cras_iodev *iodev);
struct cras_audio_format *format;
struct cras_audio_format *ext_format;
struct rate_estimator *rate_est;
@@ -159,6 +219,7 @@ struct cras_iodev {
int is_enabled;
int software_volume_needed;
struct dev_stream *streams;
+ enum CRAS_IODEV_STATE state;
unsigned int min_cb_level;
unsigned int max_cb_level;
struct buffer_share *buf_state;
@@ -167,10 +228,44 @@ struct cras_iodev {
loopback_hook_t post_dsp_hook;
void *pre_dsp_hook_cb_data;
void *post_dsp_hook_cb_data;
+ int reset_request_pending;
+ struct cras_ramp* ramp;
struct cras_iodev *prev, *next;
};
/*
+ * Ramp request used in cras_iodev_start_ramp.
+ *
+ * - CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE: Mute->unmute.
+ * Change device to unmute state after ramping is stared,
+ * that is, (a) in the plot.
+ *
+ * ____
+ * .... /
+ * _____/
+ * (a)
+ *
+ * - CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE: Unmute->mute.
+ * Change device to mute state after ramping is done, that is,
+ * (b) in the plot.
+ *
+ * _____
+ * \....
+ * \____
+ * (b)
+ *
+ * - CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK: Ramping is requested because
+ * first sample of new stream is ready, there is no need to change mute/unmute
+ * state.
+ */
+
+enum CRAS_IODEV_RAMP_REQUEST {
+ CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE = 0,
+ CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE = 1,
+ CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK = 2,
+};
+
+/*
* Utility functions to be used by iodev implementations.
*/
@@ -251,6 +346,22 @@ void cras_iodev_set_capture_timestamp(size_t frame_rate,
*/
void cras_iodev_update_dsp(struct cras_iodev *iodev);
+
+/* Sets swap mode on a node using dsp. This function can be called when
+ * dsp pipline is not created yet. It will take effect when dsp pipeline
+ * is created later. If there is dsp pipeline, this function causes the dsp
+ * pipeline to be reloaded and swap mode takes effect right away.
+ * Args:
+ * iodev - device to be changed for swap mode.
+ * node - the node to be changed for swap mode.
+ * enable - 1 to enable swap mode, 0 otherwise.
+ * Returns:
+ * 0 on success, error code on failure.
+ */
+int cras_iodev_dsp_set_swap_mode_for_node(struct cras_iodev *iodev,
+ struct cras_ionode *node,
+ int enable);
+
/* Handles a plug event happening on this node.
* Args:
* node - ionode on which a plug event was detected.
@@ -303,6 +414,16 @@ static inline unsigned int cras_iodev_adjust_active_node_volume(
return cras_iodev_adjust_node_volume(iodev->active_node, system_volume);
}
+/* Get the gain adjusted based on system for the active node. */
+static inline long cras_iodev_adjust_active_node_gain(
+ const struct cras_iodev *iodev, long system_gain)
+{
+ if (!iodev->active_node)
+ return system_gain;
+
+ return iodev->active_node->capture_gain + system_gain;
+}
+
/* Returns true if the active node of the iodev needs software volume. */
static inline int cras_iodev_software_volume_needed(
const struct cras_iodev *iodev)
@@ -316,6 +437,30 @@ static inline int cras_iodev_software_volume_needed(
return iodev->active_node->software_volume_needed;
}
+/* Returns maximum software gain for the iodev.
+ * Args:
+ * iodev - The device.
+ * Returs:
+ * 0 if software gain is not needed, or if there is no active node.
+ * Returns max_software_gain on active node if there is one. */
+static inline long cras_iodev_maximum_software_gain(
+ const struct cras_iodev *iodev)
+{
+ if (!cras_iodev_software_volume_needed(iodev))
+ return 0;
+ if (!iodev->active_node)
+ return 0;
+ return iodev->active_node->max_software_gain;
+}
+
+/* Gets the software gain scaler should be applied on the deivce.
+ * Args:
+ * iodev - The device.
+ * Returns:
+ * A scaler translated from system gain and active node gain dBm value.
+ * Returns 1.0 if software gain is not needed. */
+float cras_iodev_get_software_gain_scaler(const struct cras_iodev *iodev);
+
/* Gets the software volume scaler of the iodev. The scaler should only be
* applied if the device needs software volume. */
float cras_iodev_get_software_volume_scaler(struct cras_iodev *iodev);
@@ -346,6 +491,9 @@ void cras_iodev_stream_written(struct cras_iodev *iodev,
*/
unsigned int cras_iodev_all_streams_written(struct cras_iodev *iodev);
+/* Return the state of an iodev. */
+enum CRAS_IODEV_STATE cras_iodev_state(const struct cras_iodev *iodev);
+
/* Open an iodev, does setup and invokes the open_dev callback. */
int cras_iodev_open(struct cras_iodev *iodev, unsigned int cb_level);
@@ -383,7 +531,8 @@ int cras_iodev_get_output_buffer(struct cras_iodev *iodev,
unsigned *frames);
/* Update the estimated sample rate of the device. */
-int cras_iodev_update_rate(struct cras_iodev *iodev, unsigned int level);
+int cras_iodev_update_rate(struct cras_iodev *iodev, unsigned int level,
+ struct timespec *level_tstamp);
/* Resets the rate estimator of the device. */
int cras_iodev_reset_rate_estimator(const struct cras_iodev *iodev);
@@ -395,8 +544,16 @@ double cras_iodev_get_est_rate_ratio(const struct cras_iodev *iodev);
/* Get the delay from DSP processing in frames. */
int cras_iodev_get_dsp_delay(const struct cras_iodev *iodev);
-/* Returns the number of frames in the hardware buffer. */
-int cras_iodev_frames_queued(struct cras_iodev *iodev);
+/* Returns the number of frames in the hardware buffer.
+ * Args:
+ * iodev - The device.
+ * tstamp - The associated hardware time stamp.
+ * Returns:
+ * Number of frames in the hardware buffer.
+ * Returns -EPIPE if there is severe underrun.
+ */
+int cras_iodev_frames_queued(struct cras_iodev *iodev,
+ struct timespec *tstamp);
/* Get the delay for input/output in frames. */
static inline int cras_iodev_delay_frames(const struct cras_iodev *iodev)
@@ -407,7 +564,7 @@ static inline int cras_iodev_delay_frames(const struct cras_iodev *iodev)
/* Returns true if the device is open. */
static inline int cras_iodev_is_open(const struct cras_iodev *iodev)
{
- if (iodev && iodev->is_open(iodev))
+ if (iodev && iodev->state != CRAS_IODEV_STATE_CLOSE)
return 1;
return 0;
}
@@ -422,4 +579,126 @@ void cras_iodev_register_post_dsp_hook(struct cras_iodev *iodev,
loopback_hook_t loop_cb,
void *cb_data);
+/* Put 'frames' worth of zero samples into odev. */
+int cras_iodev_fill_odev_zeros(struct cras_iodev *odev, unsigned int frames);
+
+/* Gets the number of frames to play when audio thread sleeps.
+ * Args:
+ * iodev[in] - The device.
+ * hw_level[out] - Pointer to number of frames in hardware.
+ * hw_tstamp[out] - Pointer to the timestamp for hw_level.
+ * Returns:
+ * Number of frames to play in sleep for this output device.
+ */
+unsigned int cras_iodev_frames_to_play_in_sleep(struct cras_iodev *odev,
+ unsigned int *hw_level,
+ struct timespec *hw_tstamp);
+
+/* Checks if audio thread should wake for this output device.
+ * Args:
+ * iodev[in] - The output device.
+ * Returns:
+ * 1 if audio thread should wake for this output device. 0 otherwise.
+ */
+int cras_iodev_odev_should_wake(const struct cras_iodev *odev);
+
+/* The default implementation of no_stream ops.
+ * The default behavior is to fill some zeros when entering no stream state.
+ * Note that when a device in no stream state enters into no stream state again,
+ * device needs to fill some zeros again.
+ * Do nothing to leave no stream state.
+ * Args:
+ * iodev[in] - The output device.
+ * enable[in] - 1 to enter no stream playback, 0 to leave.
+ * Returns:
+ * 0 on success. Negative error code on failure.
+ * */
+int cras_iodev_default_no_stream_playback(struct cras_iodev *odev, int enable);
+
+
+/* Get current state of iodev.
+ * Args:
+ * iodev[in] - The device.
+ * Returns:
+ * One of states defined in CRAS_IODEV_STATE.
+ */
+enum CRAS_IODEV_STATE cras_iodev_state(const struct cras_iodev *iodev);
+
+/* Possibly transit state for output device.
+ * Check if this output device needs to transit from open state/no_stream state
+ * into normal run state. If device does not need transition and is still in
+ * no stream state, call no_stream ops to do its work for one cycle.
+ * Args:
+ * odev[in] - The output device.
+ * Returns:
+ * 0 on success. Negative error code on failure.
+ */
+int cras_iodev_prepare_output_before_write_samples(struct cras_iodev *odev);
+
+/* Get number of underruns recorded so far.
+ * Args:
+ * iodev[in] - The device.
+ * Returns:
+ * An unsigned int for number of underruns recorded.
+ */
+unsigned int cras_iodev_get_num_underruns(const struct cras_iodev *iodev);
+
+/* Get number of severe underruns recorded so far.
+ * Args:
+ * iodev[in] - The device.
+ * Returns:
+ * An unsigned int for number of severe underruns recorded since iodev
+ * was created.
+ */
+unsigned int cras_iodev_get_num_severe_underruns(
+ const struct cras_iodev *iodev);
+
+/* Request main thread to re-open device. This should be used in audio thread
+ * when it finds device is in a bad state. The request will be ignored if
+ * there is still a pending request.
+ * Args:
+ * iodev[in] - The device.
+ * Returns:
+ * 0 on success. Negative error code on failure.
+ */
+int cras_iodev_reset_request(struct cras_iodev* iodev);
+
+/* Handle output underrun.
+ * Args:
+ * odev[in] - The output device.
+ * Returns:
+ * 0 on success. Negative error code on failure.
+ */
+int cras_iodev_output_underrun(struct cras_iodev *odev);
+
+/* Start ramping samples up/down on a device.
+ * Args:
+ * iodev[in] - The device.
+ * request[in] - The request type. Check the docstrings of
+ * CRAS_IODEV_RAMP_REQUEST.
+ * Returns:
+ * 0 on success. Negative error code on failure.
+ */
+int cras_iodev_start_ramp(struct cras_iodev *odev,
+ enum CRAS_IODEV_RAMP_REQUEST request);
+
+/* Set iodev to mute/unmute state.
+ * Args:
+ * iodev[in] - The device.
+ * Returns:
+ * 0 on success. Negative error code on failure.
+ */
+int cras_iodev_set_mute(struct cras_iodev* iodev);
+
+/*
+ * Checks if an output iodev's volume is zero.
+ * If there is an active node, check the adjusted node volume.
+ * If there is no active node, check system volume.
+ * Args:
+ * odev[in] - The device.
+ * Returns:
+ * 1 if device's volume is 0. 0 otherwise.
+ */
+int cras_iodev_is_zero_volume(const struct cras_iodev *odev);
+
#endif /* CRAS_IODEV_H_ */
diff --git a/cras/src/server/cras_iodev_list.c b/cras/src/server/cras_iodev_list.c
index 354bfb7e..b89767ec 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_observer.h"
#include "cras_rstream.h"
#include "cras_server.h"
#include "cras_tm.h"
@@ -33,14 +34,18 @@ struct iodev_list {
/* List of enabled input/output devices.
* dev - The device.
+ * init_timer - Timer for a delayed call to init this iodev.
*/
struct enabled_dev {
struct cras_iodev *dev;
+ struct cras_timer *init_timer;
struct enabled_dev *prev, *next;
};
/* 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. */
+static struct cras_observer_client *list_observer;
/* Keep a list of enabled inputs and outputs. */
static struct enabled_dev *enabled_devs[CRAS_NUM_DIRECTIONS];
/* Keep an empty device per direction. */
@@ -48,14 +53,7 @@ static struct cras_iodev *fallback_devs[CRAS_NUM_DIRECTIONS];
/* Keep a constantly increasing index for iodevs. Index 0 is reserved
* to mean "no device". */
static uint32_t next_iodev_idx = MAX_SPECIAL_DEVICE_IDX;
-/* Called when the nodes are added/removed. */
-static struct cras_alert *nodes_changed_alert;
-/* Called when the active output/input is changed */
-static struct cras_alert *active_node_changed_alert;
-/* Call when the volume of a node changes. */
-static node_volume_callback_t node_volume_callback;
-static node_volume_callback_t node_input_gain_callback;
-static node_left_right_swapped_callback_t node_left_right_swapped_callback;
+
/* Call when a device is enabled or disabled. */
static device_enabled_callback_t device_enabled_callback;
static void *device_enabled_cb_data;
@@ -67,9 +65,9 @@ static struct stream_list *stream_list;
static struct cras_timer *idle_timer;
/* Flag to indicate that the stream list is disconnected from audio thread. */
static int stream_list_suspended = 0;
+/* If init device failed, retry after 1 second. */
+static const unsigned int INIT_DEV_DELAY_MS = 1000;
-static void nodes_changed_prepare(struct cras_alert *alert);
-static void active_node_changed_prepare(struct cras_alert *alert);
static void idle_dev_check(struct cras_timer *timer, void *data);
static struct cras_iodev *find_dev(size_t dev_index)
@@ -152,7 +150,7 @@ static int rm_dev_from_list(struct cras_iodev *dev)
DL_FOREACH(devs[dev->direction].iodevs, tmp)
if (tmp == dev) {
- if (dev->is_open(dev))
+ if (cras_iodev_is_open(dev))
return -EBUSY;
DL_DELETE(devs[dev->direction].iodevs, dev);
devs[dev->direction].size--;
@@ -178,21 +176,35 @@ static void fill_dev_list(struct iodev_list *list,
}
}
-static const char *node_type_to_str(enum CRAS_NODE_TYPE type)
+static const char *node_type_to_str(struct cras_ionode *node)
{
- switch (type) {
+ switch (node->type) {
case CRAS_NODE_TYPE_INTERNAL_SPEAKER:
return "INTERNAL_SPEAKER";
case CRAS_NODE_TYPE_HEADPHONE:
return "HEADPHONE";
case CRAS_NODE_TYPE_HDMI:
return "HDMI";
- case CRAS_NODE_TYPE_INTERNAL_MIC:
- return "INTERNAL_MIC";
+ case CRAS_NODE_TYPE_HAPTIC:
+ return "HAPTIC";
case CRAS_NODE_TYPE_MIC:
- return "MIC";
- case CRAS_NODE_TYPE_AOKR:
- return "AOKR";
+ switch (node->position) {
+ case NODE_POSITION_INTERNAL:
+ return "INTERNAL_MIC";
+ case NODE_POSITION_FRONT:
+ return "FRONT_MIC";
+ case NODE_POSITION_REAR:
+ return "REAR_MIC";
+ case NODE_POSITION_KEYBOARD:
+ return "KEYBOARD_MIC";
+ case NODE_POSITION_EXTERNAL:
+ default:
+ return "MIC";
+ }
+ case CRAS_NODE_TYPE_HOTWORD:
+ return "HOTWORD";
+ case CRAS_NODE_TYPE_LINEOUT:
+ return "LINEOUT";
case CRAS_NODE_TYPE_POST_MIX_PRE_DSP:
return "POST_MIX_LOOPBACK";
case CRAS_NODE_TYPE_POST_DSP:
@@ -201,8 +213,6 @@ static const char *node_type_to_str(enum CRAS_NODE_TYPE type)
return "USB";
case CRAS_NODE_TYPE_BLUETOOTH:
return "BLUETOOTH";
- case CRAS_NODE_TYPE_KEYBOARD_MIC:
- return "KEYBOARD_MIC";
case CRAS_NODE_TYPE_UNKNOWN:
default:
return "UNKNOWN";
@@ -231,10 +241,14 @@ static int fill_node_list(struct iodev_list *list,
node_info->volume = node->volume;
node_info->capture_gain = node->capture_gain;
node_info->left_right_swapped = node->left_right_swapped;
+ node_info->stable_id = node->stable_id;
+ node_info->stable_id_new = node->stable_id_new;
strcpy(node_info->mic_positions, node->mic_positions);
strcpy(node_info->name, node->name);
+ strcpy(node_info->active_hotword_model,
+ node->active_hotword_model);
snprintf(node_info->type, sizeof(node_info->type), "%s",
- node_type_to_str(node->type));
+ node_type_to_str(node));
node_info->type_enum = node->type;
node_info++;
i++;
@@ -270,25 +284,69 @@ static int get_dev_list(struct iodev_list *list,
/* Called when the system volume changes. Pass the current volume setting to
* the default output if it is active. */
-void sys_vol_change(void *data)
+static void sys_vol_change(void *context, int32_t volume)
{
struct cras_iodev *dev;
DL_FOREACH(devs[CRAS_STREAM_OUTPUT].iodevs, dev) {
- if (dev->set_volume && dev->is_open(dev))
+ if (dev->set_volume && cras_iodev_is_open(dev))
dev->set_volume(dev);
}
}
+/*
+ * Checks if a device should start ramping for mute/unmute change.
+ * Device must meet all the conditions:
+ *
+ * - Device is enabled in iodev_list.
+ * - Device has ramp support.
+ * - Device is in normal run state, that is, it must be running with valid
+ * streams.
+ * - Device volume, which considers both system volume and adjusted active
+ * node volume, is not zero. If device volume is zero, all the samples are
+ * suppressed to zero and there is no need to ramp.
+ */
+static int device_should_start_ramp_for_mute(const struct cras_iodev *dev)
+{
+ return (cras_iodev_list_dev_is_enabled(dev) && dev->ramp &&
+ cras_iodev_state(dev) == CRAS_IODEV_STATE_NORMAL_RUN &&
+ !cras_iodev_is_zero_volume(dev));
+}
+
/* Called when the system mute state changes. Pass the current mute setting
* to the default output if it is active. */
-void sys_mute_change(void *data)
+static void sys_mute_change(void *context, int muted, int user_muted,
+ int mute_locked)
{
struct cras_iodev *dev;
+ int should_mute = muted || user_muted;
DL_FOREACH(devs[CRAS_STREAM_OUTPUT].iodevs, dev) {
- if (dev->set_mute && dev->is_open(dev))
- dev->set_mute(dev);
+ if (device_should_start_ramp_for_mute(dev)) {
+ /*
+ * Start ramping in audio thread and set mute/unmute
+ * state on device. This should only be done when
+ * device is running with valid streams.
+ *
+ * 1. Mute -> Unmute: Set device unmute state after
+ * ramping is started.
+ * 2. Unmute -> Mute: Set device mute state after
+ * ramping is done.
+ *
+ * The above transition will be handled by
+ * cras_iodev_ramp_start.
+ */
+ audio_thread_dev_start_ramp(
+ audio_thread,
+ dev,
+ (should_mute ?
+ CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE :
+ CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE));
+
+ } else {
+ /* For device without ramp, just set its mute state. */
+ cras_iodev_set_mute(dev);
+ }
}
}
@@ -297,7 +355,7 @@ static int dev_has_pinned_stream(unsigned int dev_idx)
const struct cras_rstream *rstream;
DL_FOREACH(stream_list_get(stream_list), rstream) {
- if (rstream->pinned_dev_idx == dev_idx)
+ if (rstream->is_pinned && (rstream->pinned_dev_idx == dev_idx))
return 1;
}
return 0;
@@ -431,9 +489,9 @@ static void resume_devs()
}
/* Called when the system audio is suspended or resumed. */
-void sys_suspend_change(void *data)
+void sys_suspend_change(void *arg, int suspended)
{
- if (cras_system_get_suspended())
+ if (suspended)
suspend_devs();
else
resume_devs();
@@ -441,28 +499,122 @@ void sys_suspend_change(void *data)
/* Called when the system capture gain changes. Pass the current capture_gain
* setting to the default input if it is active. */
-void sys_cap_gain_change(void *data)
+void sys_cap_gain_change(void *context, int32_t gain)
{
struct cras_iodev *dev;
DL_FOREACH(devs[CRAS_STREAM_INPUT].iodevs, dev) {
- if (dev->set_capture_gain && dev->is_open(dev))
+ if (dev->set_capture_gain && cras_iodev_is_open(dev))
dev->set_capture_gain(dev);
}
}
/* Called when the system capture mute state changes. Pass the current capture
* mute setting to the default input if it is active. */
-void sys_cap_mute_change(void *data)
+static void sys_cap_mute_change(void *context, int muted, int mute_locked)
{
struct cras_iodev *dev;
DL_FOREACH(devs[CRAS_STREAM_INPUT].iodevs, dev) {
- if (dev->set_capture_mute && dev->is_open(dev))
+ if (dev->set_capture_mute && cras_iodev_is_open(dev))
dev->set_capture_mute(dev);
}
}
+static int disable_device(struct enabled_dev *edev);
+static int enable_device(struct cras_iodev *dev);
+
+static void possibly_disable_fallback(enum CRAS_STREAM_DIRECTION dir)
+{
+ struct enabled_dev *edev;
+
+ DL_FOREACH(enabled_devs[dir], edev) {
+ if (edev->dev == fallback_devs[dir])
+ disable_device(edev);
+ }
+}
+
+static void possibly_enable_fallback(enum CRAS_STREAM_DIRECTION dir)
+{
+ if (!cras_iodev_list_dev_is_enabled(fallback_devs[dir]))
+ enable_device(fallback_devs[dir]);
+}
+
+static int init_and_attach_streams(struct cras_iodev *dev)
+{
+ int rc;
+ enum CRAS_STREAM_DIRECTION dir = dev->direction;
+ struct cras_rstream *stream;
+
+ /* If called after suspend, for example bluetooth
+ * profile switching, don't add back the stream list. */
+ if (!stream_list_suspended) {
+ /* If there are active streams to attach to this device,
+ * open it. */
+ DL_FOREACH(stream_list_get(stream_list), stream) {
+ if (stream->direction != dir || stream->is_pinned)
+ continue;
+ rc = init_device(dev, stream);
+ if (rc) {
+ syslog(LOG_ERR, "Enable %s failed, rc = %d",
+ dev->info.name, rc);
+ return rc;
+ }
+ audio_thread_add_stream(audio_thread,
+ stream, &dev, 1);
+ }
+ }
+ return 0;
+}
+
+static void init_device_cb(struct cras_timer *timer, void *arg)
+{
+ int rc;
+ struct enabled_dev *edev = (struct enabled_dev *)arg;
+ struct cras_iodev *dev = edev->dev;
+
+ edev->init_timer = NULL;
+ if (cras_iodev_is_open(dev))
+ return;
+
+ rc = init_and_attach_streams(dev);
+ if (rc < 0)
+ syslog(LOG_ERR, "Init device retry failed");
+ else
+ possibly_disable_fallback(dev->direction);
+}
+
+static void schedule_init_device_retry(struct enabled_dev *edev)
+{
+ struct cras_tm *tm = cras_system_state_get_tm();
+
+ if (edev->init_timer == NULL)
+ edev->init_timer = cras_tm_create_timer(
+ tm, INIT_DEV_DELAY_MS, init_device_cb, edev);
+}
+
+static int pinned_stream_added(struct cras_rstream *rstream)
+{
+ struct cras_iodev *dev;
+ int rc;
+
+ /* Check that the target device is valid for pinned streams. */
+ dev = find_dev(rstream->pinned_dev_idx);
+ if (!dev)
+ return -EINVAL;
+
+ /* Make sure the active node is configured properly, it could be
+ * disabled when last normal stream removed. */
+ dev->update_active_node(dev, dev->active_node->idx, 1);
+
+ /* Negative EAGAIN code indicates dev will be opened later. */
+ rc = init_device(dev, rstream);
+ if (rc && (rc != -EAGAIN))
+ return rc;
+
+ return audio_thread_add_stream(audio_thread, rstream, &dev, 1);
+}
+
static int stream_added_cb(struct cras_rstream *rstream)
{
struct enabled_dev *edev;
@@ -473,20 +625,8 @@ static int stream_added_cb(struct cras_rstream *rstream)
if (stream_list_suspended)
return 0;
- /* Check that the target device is valid for pinned streams. */
- if (rstream->is_pinned) {
- struct cras_iodev *dev;
- dev = find_dev(rstream->pinned_dev_idx);
- if (!dev)
- return -EINVAL;
-
- /* Negative EAGAIN code indicates dev will be opened later. */
- rc = init_device(dev, rstream);
- if (rc && (rc != -EAGAIN))
- return rc;
-
- return audio_thread_add_stream(audio_thread, rstream, &dev, 1);
- }
+ 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. */
@@ -496,17 +636,39 @@ static int stream_added_cb(struct cras_rstream *rstream)
syslog(LOG_ERR, "too many enabled devices");
break;
}
- /* Negative EAGAIN code indicates dev will be opened later. */
+
rc = init_device(edev->dev, rstream);
- if (rc && (rc != -EAGAIN))
- return rc;
+ 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);
+ continue;
+ }
iodevs[num_iodevs++] = edev->dev;
}
- rc = audio_thread_add_stream(audio_thread, rstream, iodevs, num_iodevs);
- if (rc) {
- syslog(LOG_ERR, "adding stream to thread fail");
- return rc;
+ if (num_iodevs) {
+ rc = audio_thread_add_stream(audio_thread, rstream,
+ iodevs, num_iodevs);
+ if (rc) {
+ syslog(LOG_ERR, "adding stream to thread fail");
+ return rc;
+ }
+ } else {
+ /* Enable fallback device if no other iodevs can be initialized
+ * 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
+ * so client won't be blocked.
+ * The enabled fallback device will be disabled when
+ * cras_iodev_list_select_node() is called to re-select the
+ * active node.
+ */
+ possibly_enable_fallback(rstream->direction);
}
return 0;
}
@@ -545,8 +707,10 @@ static void pinned_stream_removed(struct cras_rstream *rstream)
struct cras_iodev *dev;
dev = find_dev(rstream->pinned_dev_idx);
- if (!cras_iodev_list_dev_is_enabled(dev))
+ if (!cras_iodev_list_dev_is_enabled(dev)) {
close_dev(dev);
+ dev->update_active_node(dev, dev->active_node->idx, 0);
+ }
}
/* Returns the number of milliseconds left to drain this stream. This is passed
@@ -568,22 +732,10 @@ static int stream_removed_cb(struct cras_rstream *rstream)
return 0;
}
-static int disable_device(struct enabled_dev *edev);
-
-static void possibly_disable_fallback(enum CRAS_STREAM_DIRECTION dir)
-{
- struct enabled_dev *edev;
-
- DL_FOREACH(enabled_devs[dir], edev) {
- if (edev->dev == fallback_devs[dir])
- disable_device(edev);
- }
-}
-
static int enable_device(struct cras_iodev *dev)
{
+ int rc;
struct enabled_dev *edev;
- struct cras_rstream *stream;
enum CRAS_STREAM_DIRECTION dir = dev->direction;
DL_FOREACH(enabled_devs[dir], edev) {
@@ -593,22 +745,16 @@ static int enable_device(struct cras_iodev *dev)
edev = calloc(1, sizeof(*edev));
edev->dev = dev;
+ edev->init_timer = NULL;
DL_APPEND(enabled_devs[dir], edev);
dev->is_enabled = 1;
- /* If enable_device is called after suspend, for example bluetooth
- * profile switching, don't add back the stream list. */
- if (!stream_list_suspended) {
- /* If there are active streams to attach to this device,
- * open it. */
- DL_FOREACH(stream_list_get(stream_list), stream) {
- if (stream->direction == dir && !stream->is_pinned) {
- init_device(dev, stream);
- audio_thread_add_stream(audio_thread,
- stream, &dev, 1);
- }
- }
+ rc = init_and_attach_streams(dev);
+ if (rc < 0) {
+ schedule_init_device_retry(edev);
+ return rc;
}
+
if (device_enabled_callback)
device_enabled_callback(dev, 1, device_enabled_cb_data);
@@ -622,6 +768,11 @@ static int disable_device(struct enabled_dev *edev)
struct cras_rstream *stream;
DL_DELETE(enabled_devs[dir], edev);
+ if (edev->init_timer) {
+ cras_tm_cancel_timer(cras_system_state_get_tm(),
+ edev->init_timer);
+ edev->init_timer = NULL;
+ }
free(edev);
dev->is_enabled = 0;
@@ -634,6 +785,7 @@ static int disable_device(struct enabled_dev *edev)
if (device_enabled_callback)
device_enabled_callback(dev, 0, device_enabled_cb_data);
close_dev(dev);
+ dev->update_active_node(dev, dev->active_node->idx, 0);
return 0;
}
@@ -644,14 +796,16 @@ static int disable_device(struct enabled_dev *edev)
void cras_iodev_list_init()
{
- cras_system_register_volume_changed_cb(sys_vol_change, NULL);
- cras_system_register_mute_changed_cb(sys_mute_change, NULL);
- cras_system_register_suspend_cb(sys_suspend_change, NULL);
- cras_system_register_capture_gain_changed_cb(sys_cap_gain_change, NULL);
- cras_system_register_capture_mute_changed_cb(sys_cap_mute_change, NULL);
- nodes_changed_alert = cras_alert_create(nodes_changed_prepare);
- active_node_changed_alert = cras_alert_create(
- active_node_changed_prepare);
+ struct cras_observer_ops observer_ops;
+
+ memset(&observer_ops, 0, sizeof(observer_ops));
+ observer_ops.output_volume_changed = sys_vol_change;
+ observer_ops.output_mute_changed = sys_mute_change;
+ observer_ops.capture_gain_changed = sys_cap_gain_change;
+ observer_ops.capture_mute_changed = sys_cap_mute_change;
+ observer_ops.suspend_changed = sys_suspend_change;
+ list_observer = cras_observer_add(&observer_ops, NULL);
+ idle_timer = NULL;
/* Create the audio stream list for the system. */
stream_list = stream_list_create(stream_added_cb, stream_removed_cb,
@@ -684,20 +838,15 @@ void cras_iodev_list_init()
void cras_iodev_list_deinit()
{
- cras_system_remove_volume_changed_cb(sys_vol_change, NULL);
- cras_system_remove_mute_changed_cb(sys_vol_change, NULL);
- cras_system_remove_suspend_cb(sys_suspend_change, NULL);
- cras_system_remove_capture_gain_changed_cb(sys_cap_gain_change, NULL);
- cras_system_remove_capture_mute_changed_cb(sys_cap_mute_change, NULL);
- cras_alert_destroy(nodes_changed_alert);
- cras_alert_destroy(active_node_changed_alert);
- nodes_changed_alert = NULL;
- active_node_changed_alert = NULL;
+ if (list_observer) {
+ cras_observer_remove(list_observer);
+ list_observer = NULL;
+ }
audio_thread_destroy(audio_thread);
stream_list_destroy(stream_list);
}
-int cras_iodev_list_dev_is_enabled(struct cras_iodev *dev)
+int cras_iodev_list_dev_is_enabled(const struct cras_iodev *dev)
{
struct enabled_dev *edev;
@@ -713,7 +862,7 @@ void cras_iodev_list_enable_dev(struct cras_iodev *dev)
{
possibly_disable_fallback(dev->direction);
enable_device(dev);
- cras_iodev_list_notify_active_node_changed();
+ cras_iodev_list_notify_active_node_changed(dev->direction);
}
void cras_iodev_list_add_active_node(enum CRAS_STREAM_DIRECTION dir,
@@ -724,23 +873,36 @@ void cras_iodev_list_add_active_node(enum CRAS_STREAM_DIRECTION dir,
if (!new_dev || new_dev->direction != dir)
return;
- new_dev->update_active_node(new_dev, node_index_of(node_id));
+ new_dev->update_active_node(new_dev, node_index_of(node_id), 1);
cras_iodev_list_enable_dev(new_dev);
}
void cras_iodev_list_disable_dev(struct cras_iodev *dev)
{
- struct enabled_dev *edev;
+ struct enabled_dev *edev, *edev_to_disable = NULL;
+
+ int is_the_only_enabled_device = 1;
DL_FOREACH(enabled_devs[dev->direction], edev) {
- if (edev->dev == dev) {
- disable_device(edev);
- if (!enabled_devs[dev->direction])
- enable_device(fallback_devs[dev->direction]);
- cras_iodev_list_notify_active_node_changed();
- return;
- }
+ if (edev->dev == dev)
+ edev_to_disable = edev;
+ else
+ is_the_only_enabled_device = 0;
}
+
+ if (!edev_to_disable)
+ return;
+
+ /* If the device to be closed is the only enabled device, we should
+ * enable the fallback device first then disable the target
+ * device. */
+ if (is_the_only_enabled_device)
+ enable_device(fallback_devs[dev->direction]);
+
+ disable_device(edev_to_disable);
+
+ cras_iodev_list_notify_active_node_changed(dev->direction);
+ return;
}
void cras_iodev_list_rm_active_node(enum CRAS_STREAM_DIRECTION dir,
@@ -866,46 +1028,45 @@ void cras_iodev_list_update_device_list()
cras_system_state_update_complete();
}
-int cras_iodev_list_register_nodes_changed_cb(cras_alert_cb cb, void *arg)
+char *cras_iodev_list_get_hotword_models(cras_node_id_t node_id)
{
- return cras_alert_add_callback(nodes_changed_alert, cb, arg);
-}
+ struct cras_iodev *dev = NULL;
-int cras_iodev_list_remove_nodes_changed_cb(cras_alert_cb cb, void *arg)
-{
- return cras_alert_rm_callback(nodes_changed_alert, cb, arg);
-}
-
-void cras_iodev_list_notify_nodes_changed()
-{
- cras_alert_pending(nodes_changed_alert);
-}
+ dev = find_dev(dev_index_of(node_id));
+ if (!dev || !dev->get_hotword_models ||
+ (dev->active_node->type != CRAS_NODE_TYPE_HOTWORD))
+ return NULL;
-static void nodes_changed_prepare(struct cras_alert *alert)
-{
- cras_iodev_list_update_device_list();
+ return dev->get_hotword_models(dev);
}
-int cras_iodev_list_register_active_node_changed_cb(cras_alert_cb cb,
- void *arg)
+int cras_iodev_list_set_hotword_model(cras_node_id_t node_id,
+ const char *model_name)
{
- return cras_alert_add_callback(active_node_changed_alert, cb, arg);
-}
+ int ret;
+ struct cras_iodev *dev =
+ find_dev(dev_index_of(node_id));
+ if (!dev || !dev->get_hotword_models ||
+ (dev->active_node->type != CRAS_NODE_TYPE_HOTWORD))
+ return -EINVAL;
-int cras_iodev_list_remove_active_node_changed_cb(cras_alert_cb cb,
- void *arg)
-{
- return cras_alert_rm_callback(active_node_changed_alert, cb, arg);
+ ret = dev->set_hotword_model(dev, model_name);
+ if (!ret)
+ strncpy(dev->active_node->active_hotword_model, model_name,
+ sizeof(dev->active_node->active_hotword_model) - 1);
+ return ret;
}
-void cras_iodev_list_notify_active_node_changed()
+void cras_iodev_list_notify_nodes_changed()
{
- cras_alert_pending(active_node_changed_alert);
+ cras_observer_notify_nodes();
}
-static void active_node_changed_prepare(struct cras_alert *alert)
+void cras_iodev_list_notify_active_node_changed(
+ enum CRAS_STREAM_DIRECTION direction)
{
- cras_iodev_list_update_device_list();
+ cras_observer_notify_active_node(direction,
+ cras_iodev_list_get_active_node_id(direction));
}
void cras_iodev_list_select_node(enum CRAS_STREAM_DIRECTION direction,
@@ -913,6 +1074,8 @@ void cras_iodev_list_select_node(enum CRAS_STREAM_DIRECTION direction,
{
struct cras_iodev *new_dev = NULL;
struct enabled_dev *edev;
+ int new_node_already_enabled = 0;
+ int rc;
/* find the devices for the id. */
new_dev = find_dev(dev_index_of(node_id));
@@ -925,16 +1088,46 @@ void cras_iodev_list_select_node(enum CRAS_STREAM_DIRECTION direction,
if (new_dev && new_dev->direction != direction)
return;
- DL_FOREACH(enabled_devs[direction], edev)
- disable_device(edev);
+ /* Determine whether the new device and node are already enabled - if
+ * they are, the selection algorithm should avoid disabling the new
+ * device. */
+ DL_FOREACH(enabled_devs[direction], edev) {
+ if (edev->dev == new_dev &&
+ edev->dev->active_node->idx == node_index_of(node_id)) {
+ new_node_already_enabled = 1;
+ break;
+ }
+ }
- if (new_dev) {
- new_dev->update_active_node(new_dev, node_index_of(node_id));
- enable_device(new_dev);
- } else {
- enable_device(fallback_devs[direction]);
+ /* Enable fallback device during the transition so client will not be
+ * blocked in this duration, which is as long as 300 ms on some boards
+ * before new device is opened.
+ * Note that the fallback node is not needed if the new node is already
+ * enabled - the new node will remain enabled. */
+ if (!new_node_already_enabled)
+ possibly_enable_fallback(direction);
+
+ /* Disable all devices except for fallback device, and the new device,
+ * provided it is already enabled. */
+ DL_FOREACH(enabled_devs[direction], edev) {
+ if (edev->dev != fallback_devs[direction] &&
+ !(new_node_already_enabled && edev->dev == new_dev)) {
+ disable_device(edev);
+ }
}
- cras_iodev_list_notify_active_node_changed();
+
+ if (new_dev && !new_node_already_enabled) {
+ new_dev->update_active_node(new_dev, node_index_of(node_id), 1);
+ rc = enable_device(new_dev);
+ if (rc == 0) {
+ /* Disable fallback device after new device is enabled.
+ * Leave the fallback device enabled if new_dev failed
+ * to open, or the new_dev == NULL case. */
+ possibly_disable_fallback(direction);
+ }
+ }
+
+ cras_iodev_list_notify_active_node_changed(direction);
}
int cras_iodev_list_set_node_attr(cras_node_id_t node_id,
@@ -948,45 +1141,29 @@ int cras_iodev_list_set_node_attr(cras_node_id_t node_id,
return -EINVAL;
rc = cras_iodev_set_node_attr(node, attr, value);
- cras_iodev_list_notify_nodes_changed();
return rc;
}
-void cras_iodev_list_set_node_volume_callbacks(node_volume_callback_t volume_cb,
- node_volume_callback_t gain_cb)
-{
- node_volume_callback = volume_cb;
- node_input_gain_callback = gain_cb;
-}
-
-void cras_iodev_list_set_node_left_right_swapped_callbacks(
- node_left_right_swapped_callback_t swapped_cb)
-{
- node_left_right_swapped_callback = swapped_cb;
-}
-
void cras_iodev_list_notify_node_volume(struct cras_ionode *node)
{
cras_node_id_t id = cras_make_node_id(node->dev->info.idx, node->idx);
-
- if (node_volume_callback)
- node_volume_callback(id, node->volume);
+ cras_iodev_list_update_device_list();
+ cras_observer_notify_output_node_volume(id, node->volume);
}
void cras_iodev_list_notify_node_left_right_swapped(struct cras_ionode *node)
{
cras_node_id_t id = cras_make_node_id(node->dev->info.idx, node->idx);
-
- if (node_left_right_swapped_callback)
- node_left_right_swapped_callback(id, node->left_right_swapped);
+ cras_iodev_list_update_device_list();
+ cras_observer_notify_node_left_right_swapped(id,
+ node->left_right_swapped);
}
void cras_iodev_list_notify_node_capture_gain(struct cras_ionode *node)
{
cras_node_id_t id = cras_make_node_id(node->dev->info.idx, node->idx);
-
- if (node_input_gain_callback)
- node_input_gain_callback(id, node->capture_gain);
+ cras_iodev_list_update_device_list();
+ cras_observer_notify_input_node_gain(id, node->capture_gain);
}
void cras_iodev_list_add_test_dev(enum TEST_IODEV_TYPE type)
@@ -1022,6 +1199,12 @@ struct stream_list *cras_iodev_list_get_stream_list()
int cras_iodev_list_set_device_enabled_callback(
device_enabled_callback_t device_enabled_cb, void *cb_data)
{
+ if (!device_enabled_cb) {
+ device_enabled_callback = NULL;
+ device_enabled_cb_data = NULL;
+ return 0;
+ }
+
/* TODO(chinyue): Allow multiple callbacks to be registered. */
if (device_enabled_callback) {
syslog(LOG_ERR, "Device enabled callback already registered.");
diff --git a/cras/src/server/cras_iodev_list.h b/cras/src/server/cras_iodev_list.h
index 1c6af8aa..3599f1b3 100644
--- a/cras/src/server/cras_iodev_list.h
+++ b/cras/src/server/cras_iodev_list.h
@@ -11,7 +11,6 @@
#include <stdint.h>
-#include "cras_alert.h"
#include "cras_types.h"
struct cras_iodev;
@@ -22,9 +21,6 @@ struct cras_rstream;
struct cras_audio_format;
struct stream_list;
-typedef void (*node_volume_callback_t)(cras_node_id_t, int);
-typedef void (*node_left_right_swapped_callback_t)(cras_node_id_t, int);
-
/* Device enabled/disabled callback.
* enabled=1 when a device is enabled, enabled=0 when a device is disabled.
*/
@@ -117,41 +113,23 @@ void cras_iodev_list_update_device_list();
/* Stores the node list in the shared memory server state region. */
void cras_iodev_list_update_node_list();
-/* Adds a callback to call when the nodes are added/removed.
- * Args:
- * cb - Function to call when there is a change.
- * arg - Value to pass back to callback.
- */
-int cras_iodev_list_register_nodes_changed_cb(cras_alert_cb cb, void *arg);
+/* Gets the supported hotword models of an ionode. Caller should free
+ * the returned string after use. */
+char *cras_iodev_list_get_hotword_models(cras_node_id_t node_id);
-/* Removes a callback to call when the nodes are added/removed.
- * Args:
- * cb - Function to call when there is a change.
- * arg - Value to pass back to callback.
- */
-int cras_iodev_list_remove_nodes_changed_cb(cras_alert_cb cb, void *arg);
+/* Sets the desired hotword model to an ionode. */
+int cras_iodev_list_set_hotword_model(cras_node_id_t id,
+ const char *model_name);
/* Notify that nodes are added/removed. */
void cras_iodev_list_notify_nodes_changed();
-/* Adds a callback to call when the active output/input node changes.
+/* Notify that active node is changed for the given direction.
* Args:
- * cb - Function to call when there is a change.
- * arg - Value to pass back to callback.
+ * direction - Direction of the node.
*/
-int cras_iodev_list_register_active_node_changed_cb(cras_alert_cb cb,
- void *arg);
-
-/* Removes a callback to call when the active output/input node changes.
- * Args:
- * cb - Function to call when there is a change.
- * arg - Value to pass back to callback.
- */
-int cras_iodev_list_remove_active_node_changed_cb(cras_alert_cb cb,
- void *arg);
-
-/* Notify that active output/input node is changed. */
-void cras_iodev_list_notify_active_node_changed();
+void cras_iodev_list_notify_active_node_changed(
+ enum CRAS_STREAM_DIRECTION direction);
/* Sets an attribute of an ionode on a device.
* Args:
@@ -173,7 +151,7 @@ void cras_iodev_list_select_node(enum CRAS_STREAM_DIRECTION direction,
cras_node_id_t node_id);
/* Checks if an iodev is enabled. */
-int cras_iodev_list_dev_is_enabled(struct cras_iodev *dev);
+int cras_iodev_list_dev_is_enabled(const struct cras_iodev *dev);
/* Enables an iodev. If the fallback device was already enabled, this
* call will disable it. */
@@ -202,21 +180,12 @@ void cras_iodev_list_rm_active_node(enum CRAS_STREAM_DIRECTION direction,
/* Returns 1 if the node is selected, 0 otherwise. */
int cras_iodev_list_node_selected(struct cras_ionode *node);
-/* Sets the function to call when a node volume changes. */
-void cras_iodev_list_set_node_volume_callbacks(node_volume_callback_t volume_cb,
- node_volume_callback_t gain_cb);
-
/* Notify the current volume of the given node. */
void cras_iodev_list_notify_node_volume(struct cras_ionode *node);
/* Notify the current capture gain of the given node. */
void cras_iodev_list_notify_node_capture_gain(struct cras_ionode *node);
-/* Sets the function to call when a node's left right channel swapping state
- * is changes. */
-void cras_iodev_list_set_node_left_right_swapped_callbacks(
- node_left_right_swapped_callback_t swapped_cb);
-
/* Notify the current left right channel swapping state of the given node. */
void cras_iodev_list_notify_node_left_right_swapped(struct cras_ionode *node);
diff --git a/cras/src/server/cras_loopback_iodev.c b/cras/src/server/cras_loopback_iodev.c
index bdc21f7d..ff33acaf 100644
--- a/cras/src/server/cras_loopback_iodev.c
+++ b/cras/src/server/cras_loopback_iodev.c
@@ -38,13 +38,11 @@ static snd_pcm_format_t loopback_supported_formats[] = {
};
/* loopack iodev. Keep state of a loopback device.
- * open - Is the device open.
* sample_buffer - Pointer to sample buffer.
*/
struct loopback_iodev {
struct cras_iodev base;
enum CRAS_LOOPBACK_TYPE loopback_type;
- int open;
struct timespec last_filled;
struct byte_buffer *sample_buffer;
};
@@ -119,19 +117,8 @@ static void device_enabled_hook(struct cras_iodev *iodev, int enabled,
* iodev callbacks.
*/
-static int is_open(const struct cras_iodev *iodev)
-{
- struct loopback_iodev *loopdev = (struct loopback_iodev *)iodev;
-
- return loopdev && loopdev->open;
-}
-
-static int dev_running(const struct cras_iodev *iodev)
-{
- return is_open(iodev);
-}
-
-static int frames_queued(const struct cras_iodev *iodev)
+static int frames_queued(const struct cras_iodev *iodev,
+ struct timespec *hw_tstamp)
{
struct loopback_iodev *loopdev = (struct loopback_iodev *)iodev;
struct byte_buffer *sbuf = loopdev->sample_buffer;
@@ -155,13 +142,15 @@ static int frames_queued(const struct cras_iodev *iodev)
&loopdev->last_filled);
}
}
-
+ *hw_tstamp = loopdev->last_filled;
return buf_queued_bytes(sbuf) / frame_bytes;
}
static int delay_frames(const struct cras_iodev *iodev)
{
- return frames_queued(iodev);
+ struct timespec tstamp;
+
+ return frames_queued(iodev, &tstamp);
}
static int close_record_dev(struct cras_iodev *iodev)
@@ -170,7 +159,6 @@ static int close_record_dev(struct cras_iodev *iodev)
struct byte_buffer *sbuf = loopdev->sample_buffer;
struct cras_iodev *edev;
- loopdev->open = 0;
cras_iodev_free_format(iodev);
cras_iodev_free_audio_area(iodev);
buf_reset(sbuf);
@@ -188,7 +176,6 @@ static int open_record_dev(struct cras_iodev *iodev)
struct cras_iodev *edev;
cras_iodev_init_audio_area(iodev, iodev->format->num_channels);
- loopdev->open = 1;
clock_gettime(CLOCK_MONOTONIC_RAW, &loopdev->last_filled);
edev = cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT);
@@ -207,8 +194,9 @@ static int get_record_buffer(struct cras_iodev *iodev,
struct loopback_iodev *loopdev = (struct loopback_iodev *)iodev;
struct byte_buffer *sbuf = loopdev->sample_buffer;
unsigned int frame_bytes = cras_get_format_bytes(iodev->format);
+ unsigned int avail_frames = buf_readable_bytes(sbuf) / frame_bytes;
- *frames = buf_readable_bytes(sbuf) / frame_bytes;
+ *frames = MIN(avail_frames, *frames);
iodev->area->frames = *frames;
cras_audio_area_config_buf_pointers(iodev->area, iodev->format,
buf_read_pointer(sbuf));
@@ -237,7 +225,8 @@ static int flush_record_buffer(struct cras_iodev *iodev)
return 0;
}
-static void update_active_node(struct cras_iodev *iodev, unsigned node_idx)
+static void update_active_node(struct cras_iodev *iodev, unsigned node_idx,
+ unsigned dev_enabled)
{
}
@@ -266,14 +255,13 @@ static struct cras_iodev *create_loopback_iodev(enum CRAS_LOOPBACK_TYPE type)
iodev->info.stable_id = SuperFastHash(iodev->info.name,
strlen(iodev->info.name),
strlen(iodev->info.name));
+ iodev->info.stable_id_new = iodev->info.stable_id;
iodev->supported_rates = loopback_supported_rates;
iodev->supported_channel_counts = loopback_supported_channel_counts;
iodev->supported_formats = loopback_supported_formats;
iodev->buffer_size = LOOPBACK_BUFFER_SIZE;
- iodev->is_open = is_open;
- iodev->dev_running = dev_running;
iodev->frames_queued = frames_queued;
iodev->delay_frames = delay_frames;
iodev->update_active_node = update_active_node;
@@ -317,6 +305,10 @@ struct cras_iodev *loopback_iodev_create(enum CRAS_LOOPBACK_TYPE type)
node->type = node_type;
node->plugged = 1;
node->volume = 100;
+ node->stable_id = iodev->info.stable_id;
+ node->stable_id_new = iodev->info.stable_id_new;
+ node->software_volume_needed = 0;
+ node->max_software_gain = 0;
strcpy(node->name, loopdev_names[type]);
cras_iodev_add_node(iodev, node);
cras_iodev_set_active_node(iodev, node);
diff --git a/cras/src/server/cras_main_message.c b/cras/src/server/cras_main_message.c
index 950d0dbb..1187102e 100644
--- a/cras/src/server/cras_main_message.c
+++ b/cras/src/server/cras_main_message.c
@@ -114,4 +114,4 @@ void cras_main_message_init() {
cras_system_add_select_fd(main_msg_fds[0],
handle_main_messages,
NULL);
-} \ No newline at end of file
+}
diff --git a/cras/src/server/cras_main_message.h b/cras/src/server/cras_main_message.h
index f4267c1a..ba6b02a7 100644
--- a/cras/src/server/cras_main_message.h
+++ b/cras/src/server/cras_main_message.h
@@ -16,6 +16,7 @@ enum CRAS_MAIN_MESSAGE_TYPE {
CRAS_MAIN_A2DP,
CRAS_MAIN_BT,
CRAS_MAIN_METRICS,
+ CRAS_MAIN_MONITOR_DEVICE,
};
/* Structure of the header of the message handled by main thread.
@@ -43,4 +44,4 @@ int cras_main_message_add_handler(enum CRAS_MAIN_MESSAGE_TYPE type,
/* Initialize the message handling mechanism in main thread. */
void cras_main_message_init();
-#endif /* CRAS_MAIN_MESSAGE_H_ */ \ No newline at end of file
+#endif /* CRAS_MAIN_MESSAGE_H_ */
diff --git a/cras/src/server/cras_mix.c b/cras/src/server/cras_mix.c
index d2d5e3cf..b0694fdd 100644
--- a/cras/src/server/cras_mix.c
+++ b/cras/src/server/cras_mix.c
@@ -6,591 +6,75 @@
#include <stdint.h>
#include "cras_system_state.h"
+#include "cras_mix.h"
+#include "cras_mix_ops.h"
-#define MAX_VOLUME_TO_SCALE 0.9999999
-#define MIN_VOLUME_TO_SCALE 0.0000001
+static const struct cras_mix_ops *ops = &mixer_ops;
-/*
- * Signed 16 bit little endian functions.
- */
-
-static void cras_mix_add_clip_s16_le(int16_t *dst,
- const int16_t *src,
- size_t count)
+static const struct cras_mix_ops* get_mixer_ops(unsigned int cpu_flags)
{
- int32_t sum;
- size_t i;
+#if defined HAVE_FMA
+ if (cpu_flags & CPU_X86_FMA)
+ return &mixer_ops_fma;
+#endif
+#if defined HAVE_AVX2
+ if (cpu_flags & CPU_X86_AVX2)
+ return &mixer_ops_avx2;
+#endif
+#if defined HAVE_AVX
+ if (cpu_flags & CPU_X86_AVX)
+ return &mixer_ops_avx;
+#endif
+#if defined HAVE_SSE42
+ if (cpu_flags & CPU_X86_SSE4_2)
+ return &mixer_ops_sse42;
+#endif
- for (i = 0; i < count; i++) {
- sum = dst[i] + src[i];
- if (sum > INT16_MAX)
- sum = INT16_MAX;
- else if (sum < INT16_MIN)
- sum = INT16_MIN;
- dst[i] = sum;
- }
+ /* default C implementation */
+ return &mixer_ops;
}
-/* Adds src into dst, after scaling by vol.
- * Just hard limits to the min and max S16 value, can be improved later. */
-static void scale_add_clip_s16_le(int16_t *dst,
- const int16_t *src,
- size_t count,
- float vol)
+void cras_mix_init(unsigned int flags)
{
- int32_t sum;
- size_t i;
-
- if (vol > MAX_VOLUME_TO_SCALE)
- return cras_mix_add_clip_s16_le(dst, src, count);
-
- for (i = 0; i < count; i++) {
- sum = dst[i] + (int16_t)(src[i] * vol);
- if (sum > INT16_MAX)
- sum = INT16_MAX;
- else if (sum < INT16_MIN)
- sum = INT16_MIN;
- dst[i] = sum;
- }
-}
-
-/* Adds the first stream to the mix. Don't need to mix, just setup to the new
- * values. If volume is 1.0, just memcpy. */
-static void copy_scaled_s16_le(int16_t *dst,
- const int16_t *src,
- size_t count,
- float volume_scaler)
-{
- int i;
-
- if (volume_scaler > MAX_VOLUME_TO_SCALE) {
- memcpy(dst, src, count * sizeof(*src));
- return;
- }
-
- for (i = 0; i < count; i++)
- dst[i] = src[i] * volume_scaler;
-}
-
-static void cras_scale_buffer_s16_le(uint8_t *buffer, unsigned int count,
- float scaler)
-{
- int i;
- int16_t *out = (int16_t *)buffer;
-
- if (scaler > MAX_VOLUME_TO_SCALE)
- return;
-
- if (scaler < MIN_VOLUME_TO_SCALE) {
- memset(out, 0, count * sizeof(*out));
- return;
- }
-
- for (i = 0; i < count; i++)
- out[i] *= scaler;
-}
-
-static void cras_mix_add_s16_le(uint8_t *dst, uint8_t *src,
- unsigned int count, unsigned int index,
- int mute, float mix_vol)
-{
- int16_t *out = (int16_t *)dst;
- int16_t *in = (int16_t *)src;
-
- if (mute || (mix_vol < MIN_VOLUME_TO_SCALE)) {
- if (index == 0)
- memset(out, 0, count * sizeof(*out));
- return;
- }
-
- if (index == 0)
- return copy_scaled_s16_le(out, in, count, mix_vol);
-
- scale_add_clip_s16_le(out, in, count, mix_vol);
-}
-
-void cras_mix_add_stride_s16_le(uint8_t *dst, uint8_t *src,
- unsigned int dst_stride,
- unsigned int src_stride,
- unsigned int count)
-{
- unsigned int i;
-
- for (i = 0; i < count; i++) {
- int32_t sum;
- sum = *(int16_t *)dst + *(int16_t *)src;
- if (sum > INT16_MAX)
- sum = INT16_MAX;
- else if (sum < INT16_MIN)
- sum = INT16_MIN;
- *(int16_t*)dst = sum;
- dst += dst_stride;
- src += src_stride;
- }
+ ops = get_mixer_ops(flags);
}
/*
- * Signed 24 bit little endian functions.
- */
-
-static void cras_mix_add_clip_s24_le(int32_t *dst,
- const int32_t *src,
- size_t count)
-{
- int32_t sum;
- size_t i;
-
- for (i = 0; i < count; i++) {
- sum = dst[i] + src[i];
- if (sum > 0x007fffff)
- sum = 0x007fffff;
- else if (sum < (int32_t)0xff800000)
- sum = (int32_t)0xff800000;
- dst[i] = sum;
- }
-}
-
-/* Adds src into dst, after scaling by vol.
- * Just hard limits to the min and max S24 value, can be improved later. */
-static void scale_add_clip_s24_le(int32_t *dst,
- const int32_t *src,
- size_t count,
- float vol)
-{
- int32_t sum;
- size_t i;
-
- if (vol > MAX_VOLUME_TO_SCALE)
- return cras_mix_add_clip_s24_le(dst, src, count);
-
- for (i = 0; i < count; i++) {
- sum = dst[i] + (int32_t)(src[i] * vol);
- if (sum > 0x007fffff)
- sum = 0x007fffff;
- else if (sum < (int32_t)0xff800000)
- sum = (int32_t)0xff800000;
- dst[i] = sum;
- }
-}
-
-/* Adds the first stream to the mix. Don't need to mix, just setup to the new
- * values. If volume is 1.0, just memcpy. */
-static void copy_scaled_s24_le(int32_t *dst,
- const int32_t *src,
- size_t count,
- float volume_scaler)
-{
- int i;
-
- if (volume_scaler > MAX_VOLUME_TO_SCALE) {
- memcpy(dst, src, count * sizeof(*src));
- return;
- }
-
- for (i = 0; i < count; i++)
- dst[i] = src[i] * volume_scaler;
-}
-
-static void cras_scale_buffer_s24_le(uint8_t *buffer, unsigned int count,
- float scaler)
-{
- int i;
- int32_t *out = (int32_t *)buffer;
-
- if (scaler > MAX_VOLUME_TO_SCALE)
- return;
-
- if (scaler < MIN_VOLUME_TO_SCALE) {
- memset(out, 0, count * sizeof(*out));
- return;
- }
-
- for (i = 0; i < count; i++)
- out[i] *= scaler;
-}
-
-static void cras_mix_add_s24_le(uint8_t *dst, uint8_t *src,
- unsigned int count, unsigned int index,
- int mute, float mix_vol)
-{
- int32_t *out = (int32_t *)dst;
- int32_t *in = (int32_t *)src;
-
- if (mute || (mix_vol < MIN_VOLUME_TO_SCALE)) {
- if (index == 0)
- memset(out, 0, count * sizeof(*out));
- return;
- }
-
- if (index == 0)
- return copy_scaled_s24_le(out, in, count, mix_vol);
-
- scale_add_clip_s24_le(out, in, count, mix_vol);
-}
-
-void cras_mix_add_stride_s24_le(uint8_t *dst, uint8_t *src,
- unsigned int dst_stride,
- unsigned int src_stride,
- unsigned int count)
-{
- unsigned int i;
-
- for (i = 0; i < count; i++) {
- int32_t sum;
- sum = *(int32_t *)dst + *(int32_t *)src;
- if (sum > 0x007fffff)
- sum = 0x007fffff;
- else if (sum < (int32_t)0xff800000)
- sum = (int32_t)0xff800000;
- *(int32_t*)dst = sum;
- dst += dst_stride;
- src += src_stride;
- }
-}
-
-/*
- * Signed 32 bit little endian functions.
- */
-
-static void cras_mix_add_clip_s32_le(int32_t *dst,
- const int32_t *src,
- size_t count)
-{
- int64_t sum;
- size_t i;
-
- for (i = 0; i < count; i++) {
- sum = (int64_t)dst[i] + (int64_t)src[i];
- if (sum > INT32_MAX)
- sum = INT32_MAX;
- else if (sum < INT32_MIN)
- sum = INT32_MIN;
- dst[i] = sum;
- }
-}
-
-/* Adds src into dst, after scaling by vol.
- * Just hard limits to the min and max S32 value, can be improved later. */
-static void scale_add_clip_s32_le(int32_t *dst,
- const int32_t *src,
- size_t count,
- float vol)
-{
- int64_t sum;
- size_t i;
-
- if (vol > MAX_VOLUME_TO_SCALE)
- return cras_mix_add_clip_s32_le(dst, src, count);
-
- for (i = 0; i < count; i++) {
- sum = (int64_t)dst[i] + (int64_t)(src[i] * vol);
- if (sum > INT32_MAX)
- sum = INT32_MAX;
- else if (sum < INT32_MIN)
- sum = INT32_MIN;
- dst[i] = sum;
- }
-}
-
-/* Adds the first stream to the mix. Don't need to mix, just setup to the new
- * values. If volume is 1.0, just memcpy. */
-static void copy_scaled_s32_le(int32_t *dst,
- const int32_t *src,
- size_t count,
- float volume_scaler)
-{
- int i;
-
- if (volume_scaler > MAX_VOLUME_TO_SCALE) {
- memcpy(dst, src, count * sizeof(*src));
- return;
- }
-
- for (i = 0; i < count; i++)
- dst[i] = src[i] * volume_scaler;
-}
-
-static void cras_scale_buffer_s32_le(uint8_t *buffer, unsigned int count,
- float scaler)
-{
- int i;
- int32_t *out = (int32_t *)buffer;
-
- if (scaler > MAX_VOLUME_TO_SCALE)
- return;
-
- if (scaler < MIN_VOLUME_TO_SCALE) {
- memset(out, 0, count * sizeof(*out));
- return;
- }
-
- for (i = 0; i < count; i++)
- out[i] *= scaler;
-}
-
-static void cras_mix_add_s32_le(uint8_t *dst, uint8_t *src,
- unsigned int count, unsigned int index,
- int mute, float mix_vol)
-{
- int32_t *out = (int32_t *)dst;
- int32_t *in = (int32_t *)src;
-
- if (mute || (mix_vol < MIN_VOLUME_TO_SCALE)) {
- if (index == 0)
- memset(out, 0, count * sizeof(*out));
- return;
- }
-
- if (index == 0)
- return copy_scaled_s32_le(out, in, count, mix_vol);
-
- scale_add_clip_s32_le(out, in, count, mix_vol);
-}
-
-void cras_mix_add_stride_s32_le(uint8_t *dst, uint8_t *src,
- unsigned int dst_stride,
- unsigned int src_stride,
- unsigned int count)
-{
- unsigned int i;
-
- for (i = 0; i < count; i++) {
- int64_t sum;
- sum = *(int32_t *)dst + *(int32_t *)src;
- if (sum > INT32_MAX)
- sum = INT32_MAX;
- else if (sum < INT32_MIN)
- sum = INT32_MIN;
- *(int32_t*)dst = sum;
- dst += dst_stride;
- src += src_stride;
- }
-}
-
-/*
- * Signed 24 bit little endian in three bytes functions.
+ * Exported Interface
*/
-/* Convert 3bytes Signed 24bit integer to a Signed 32bit integer.
- * Just a helper function. */
-static inline void convert_single_s243le_to_s32le(int32_t *dst,
- const uint8_t *src)
-{
- *dst = 0;
- memcpy((uint8_t *)dst + 1, src, 3);
-}
-
-static inline void convert_single_s32le_to_s243le(uint8_t *dst,
- const int32_t *src)
-{
- memcpy(dst, (uint8_t *)src + 1, 3);
-}
-
-static void cras_mix_add_clip_s24_3le(uint8_t *dst,
- const uint8_t *src,
- size_t count)
-{
- int64_t sum;
- int32_t dst_frame;
- int32_t src_frame;
- size_t i;
-
- for (i = 0; i < count; i++, dst += 3, src += 3) {
- convert_single_s243le_to_s32le(&dst_frame, dst);
- convert_single_s243le_to_s32le(&src_frame, src);
- sum = (int64_t)dst_frame + (int64_t)src_frame;
- if (sum > INT32_MAX)
- sum = INT32_MAX;
- else if (sum < INT32_MIN)
- sum = INT32_MIN;
- dst_frame = (int32_t)sum;
- convert_single_s32le_to_s243le(dst, &dst_frame);
- }
-}
-
-/* Adds src into dst, after scaling by vol.
- * Just hard limits to the min and max S24 value, can be improved later. */
-static void scale_add_clip_s24_3le(uint8_t *dst,
- const uint8_t *src,
- size_t count,
- float vol)
-{
- int64_t sum;
- int32_t dst_frame;
- int32_t src_frame;
- size_t i;
-
- if (vol > MAX_VOLUME_TO_SCALE)
- return cras_mix_add_clip_s24_3le(dst, src, count);
-
- for (i = 0; i < count; i++, dst += 3, src += 3) {
- convert_single_s243le_to_s32le(&dst_frame, dst);
- convert_single_s243le_to_s32le(&src_frame, src);
- sum = (int64_t)dst_frame + (int64_t)(src_frame * vol);
- if (sum > INT32_MAX)
- sum = INT32_MAX;
- else if (sum < INT32_MIN)
- sum = INT32_MIN;
- dst_frame = (int32_t)sum;
- convert_single_s32le_to_s243le(dst, &dst_frame);
- }
-}
-
-/* Adds the first stream to the mix. Don't need to mix, just setup to the new
- * values. If volume is 1.0, just memcpy. */
-static void copy_scaled_s24_3le(uint8_t *dst,
- const uint8_t *src,
- size_t count,
- float volume_scaler)
-{
- int32_t frame;
- size_t i;
-
- if (volume_scaler > MAX_VOLUME_TO_SCALE) {
- memcpy(dst, src, 3 * count * sizeof(*src));
- return;
- }
-
- for (i = 0; i < count; i++, dst += 3, src += 3) {
- convert_single_s243le_to_s32le(&frame, src);
- frame *= volume_scaler;
- convert_single_s32le_to_s243le(dst, &frame);
- }
-}
-
-static void cras_scale_buffer_s24_3le(uint8_t *buffer, unsigned int count,
- float scaler)
+void cras_scale_buffer_increment(snd_pcm_format_t fmt, uint8_t *buff,
+ unsigned int frame, float scaler,
+ float increment, int channel)
{
- int32_t frame;
- int i;
-
- if (scaler > MAX_VOLUME_TO_SCALE)
- return;
-
- if (scaler < MIN_VOLUME_TO_SCALE) {
- memset(buffer, 0, 3 * count * sizeof(*buffer));
- return;
- }
-
- for (i = 0; i < count; i++, buffer += 3) {
- convert_single_s243le_to_s32le(&frame, buffer);
- frame *= scaler;
- convert_single_s32le_to_s243le(buffer, &frame);
- }
-}
-
-static void cras_mix_add_s24_3le(uint8_t *dst, uint8_t *src,
- unsigned int count, unsigned int index,
- int mute, float mix_vol)
-{
- uint8_t *out = dst;
- uint8_t *in = src;
-
- if (mute || (mix_vol < MIN_VOLUME_TO_SCALE)) {
- if (index == 0)
- memset(out, 0, 3 * count * sizeof(*out));
- return;
- }
-
- if (index == 0)
- return copy_scaled_s24_3le(out, in, count, mix_vol);
-
- scale_add_clip_s24_3le(out, in, count, mix_vol);
-}
-
-void cras_mix_add_stride_s24_3le(uint8_t *dst, uint8_t *src,
- unsigned int dst_stride,
- unsigned int src_stride,
- unsigned int count)
-{
- unsigned int i;
- int64_t sum;
- int32_t dst_frame;
- int32_t src_frame;
-
- for (i = 0; i < count; i++) {
- convert_single_s243le_to_s32le(&dst_frame, dst);
- convert_single_s243le_to_s32le(&src_frame, src);
- sum = (int64_t)dst_frame + (int64_t)src_frame;
- if (sum > INT32_MAX)
- sum = INT32_MAX;
- else if (sum < INT32_MIN)
- sum = INT32_MIN;
- dst_frame = (int32_t)sum;
- convert_single_s32le_to_s243le(dst, &dst_frame);
- dst += dst_stride;
- src += src_stride;
- }
+ ops->scale_buffer_increment(fmt, buff, frame * channel, scaler,
+ increment, channel);
}
-/*
- * Exported Interface
- */
void cras_scale_buffer(snd_pcm_format_t fmt, uint8_t *buff, unsigned int count,
float scaler)
{
- switch (fmt) {
- case SND_PCM_FORMAT_S16_LE:
- return cras_scale_buffer_s16_le(buff, count, scaler);
- case SND_PCM_FORMAT_S24_LE:
- return cras_scale_buffer_s24_le(buff, count, scaler);
- case SND_PCM_FORMAT_S32_LE:
- return cras_scale_buffer_s32_le(buff, count, scaler);
- case SND_PCM_FORMAT_S24_3LE:
- return cras_scale_buffer_s24_3le(buff, count, scaler);
- default:
- break;
- }
+ ops->scale_buffer(fmt, buff, count, scaler);
}
void cras_mix_add(snd_pcm_format_t fmt, uint8_t *dst, uint8_t *src,
unsigned int count, unsigned int index,
int mute, float mix_vol)
{
- switch (fmt) {
- case SND_PCM_FORMAT_S16_LE:
- return cras_mix_add_s16_le(dst, src, count, index, mute,
- mix_vol);
- case SND_PCM_FORMAT_S24_LE:
- return cras_mix_add_s24_le(dst, src, count, index, mute,
- mix_vol);
- case SND_PCM_FORMAT_S32_LE:
- return cras_mix_add_s32_le(dst, src, count, index, mute,
- mix_vol);
- case SND_PCM_FORMAT_S24_3LE:
- return cras_mix_add_s24_3le(dst, src, count, index, mute,
- mix_vol);
- default:
- break;
- }
+ ops->add(fmt, dst, src, count, index, mute, mix_vol);
}
-void cras_mix_add_stride(snd_pcm_format_t fmt, uint8_t *dst, uint8_t *src,
+void cras_mix_add_scale_stride(snd_pcm_format_t fmt, uint8_t *dst, uint8_t *src,
unsigned int count, unsigned int dst_stride,
- unsigned int src_stride)
+ unsigned int src_stride, float scaler)
{
- switch (fmt) {
- case SND_PCM_FORMAT_S16_LE:
- return cras_mix_add_stride_s16_le(dst, src, dst_stride,
- src_stride, count);
- case SND_PCM_FORMAT_S24_LE:
- return cras_mix_add_stride_s24_le(dst, src, dst_stride,
- src_stride, count);
- case SND_PCM_FORMAT_S32_LE:
- return cras_mix_add_stride_s32_le(dst, src, dst_stride,
- src_stride, count);
- case SND_PCM_FORMAT_S24_3LE:
- return cras_mix_add_stride_s24_3le(dst, src, dst_stride,
- src_stride, count);
- default:
- break;
- }
+ ops->add_scale_stride(fmt, dst, src, count, dst_stride, src_stride,
+ scaler);
}
size_t cras_mix_mute_buffer(uint8_t *dst,
size_t frame_bytes,
size_t count)
{
- memset(dst, 0, count * frame_bytes);
- return count;
+ return ops->mute_buffer(dst, frame_bytes, count);
}
diff --git a/cras/src/server/cras_mix.h b/cras/src/server/cras_mix.h
index 0013b3aa..c16614a4 100644
--- a/cras/src/server/cras_mix.h
+++ b/cras/src/server/cras_mix.h
@@ -8,6 +8,28 @@
struct cras_audio_shm;
+/* SIMD optimisation flags */
+#define CPU_X86_SSE4_2 1
+#define CPU_X86_AVX 2
+#define CPU_X86_AVX2 4
+#define CPU_X86_FMA 8
+
+void cras_mix_init(unsigned int flags);
+
+/* Scale the given buffer with the provided scaler and increment.
+ * Args:
+ * fmt - The format (SND_PCM_FORMAT_*)
+ * buff - Buffer of samples to scale.
+ * frame - The number of frames to render.
+ * scaler - Amount to scale samples (0.0 - 1.0).
+ * increment - The increment(+/-) of scaler at each frame. The scaler after
+ * increasing/descreasing will be clipped to (0.0 - 1.0).
+ * channel - Number of samples in a frame.
+ */
+void cras_scale_buffer_increment(snd_pcm_format_t fmt, uint8_t *buff,
+ unsigned int frame, float scaler,
+ float increment, int channel);
+
/* Scale the given buffer with the provided scaler.
* Args:
* fmt - The format (SND_PCM_FORMAT_*)
@@ -41,10 +63,11 @@ void cras_mix_add(snd_pcm_format_t fmt, uint8_t *dst, uint8_t *src,
* count - The number of samples to mix.
* dst_stride - Stride between channel samples in dst in bytes.
* src_stride - Stride between channel samples in src in bytes.
+ * scaler - Amount to scale samples.
*/
-void cras_mix_add_stride(snd_pcm_format_t fmt, uint8_t *dst, uint8_t *src,
+void cras_mix_add_scale_stride(snd_pcm_format_t fmt, uint8_t *dst, uint8_t *src,
unsigned int count, unsigned int dst_stride,
- unsigned int src_stride);
+ unsigned int src_stride, float scaler);
/* Mutes the given buffer.
* Args:
diff --git a/cras/src/server/cras_mix_ops.c b/cras/src/server/cras_mix_ops.c
new file mode 100644
index 00000000..f12f7838
--- /dev/null
+++ b/cras/src/server/cras_mix_ops.c
@@ -0,0 +1,862 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <stdint.h>
+
+#include "cras_system_state.h"
+#include "cras_mix_ops.h"
+
+#define MAX_VOLUME_TO_SCALE 0.9999999
+#define MIN_VOLUME_TO_SCALE 0.0000001
+
+/* function suffixes for SIMD ops */
+#ifdef OPS_SSE42
+ #define OPS(a) a ## _sse42
+#elif OPS_AVX
+ #define OPS(a) a ## _avx
+#elif OPS_AVX2
+ #define OPS(a) a ## _avx2
+#elif OPS_FMA
+ #define OPS(a) a ## _fma
+#else
+ #define OPS(a) a
+#endif
+
+/* Checks if the scaler needs a scaling operation.
+ * We skip scaling for scaler too close to 1.0.
+ * Note that this is not subjected to MAX_VOLUME_TO_SCALE
+ * and MIN_VOLUME_TO_SCALE. */
+static inline int need_to_scale(float scaler) {
+ return (scaler < 0.99 || scaler > 1.01);
+}
+
+/*
+ * Signed 16 bit little endian functions.
+ */
+
+static void cras_mix_add_clip_s16_le(int16_t *dst,
+ const int16_t *src,
+ size_t count)
+{
+ int32_t sum;
+ size_t i;
+
+ for (i = 0; i < count; i++) {
+ sum = dst[i] + src[i];
+ if (sum > INT16_MAX)
+ sum = INT16_MAX;
+ else if (sum < INT16_MIN)
+ sum = INT16_MIN;
+ dst[i] = sum;
+ }
+}
+
+/* Adds src into dst, after scaling by vol.
+ * Just hard limits to the min and max S16 value, can be improved later. */
+static void scale_add_clip_s16_le(int16_t *dst,
+ const int16_t *src,
+ size_t count,
+ float vol)
+{
+ int32_t sum;
+ size_t i;
+
+ if (vol > MAX_VOLUME_TO_SCALE)
+ return cras_mix_add_clip_s16_le(dst, src, count);
+
+ for (i = 0; i < count; i++) {
+ sum = dst[i] + (int16_t)(src[i] * vol);
+ if (sum > INT16_MAX)
+ sum = INT16_MAX;
+ else if (sum < INT16_MIN)
+ sum = INT16_MIN;
+ dst[i] = sum;
+ }
+}
+
+/* Adds the first stream to the mix. Don't need to mix, just setup to the new
+ * values. If volume is 1.0, just memcpy. */
+static void copy_scaled_s16_le(int16_t *dst,
+ const int16_t *src,
+ size_t count,
+ float volume_scaler)
+{
+ int i;
+
+ if (volume_scaler > MAX_VOLUME_TO_SCALE) {
+ memcpy(dst, src, count * sizeof(*src));
+ return;
+ }
+
+ for (i = 0; i < count; i++)
+ dst[i] = src[i] * volume_scaler;
+}
+
+static void cras_scale_buffer_inc_s16_le(uint8_t *buffer, unsigned int count,
+ float scaler, float increment, int step)
+{
+ int i = 0, j;
+ int16_t *out = (int16_t *)buffer;
+
+ if (scaler > MAX_VOLUME_TO_SCALE && increment > 0)
+ return;
+
+ if (scaler < MIN_VOLUME_TO_SCALE && increment < 0) {
+ memset(out, 0, count * sizeof(*out));
+ return;
+ }
+
+ while (i + step <= count) {
+ for (j = 0; j < step; j++) {
+ if (scaler > MAX_VOLUME_TO_SCALE) {
+ } else if (scaler < MIN_VOLUME_TO_SCALE) {
+ out[i] = 0;
+ } else {
+ out[i] *= scaler;
+ }
+ i++;
+ }
+ scaler += increment;
+ }
+}
+
+static void cras_scale_buffer_s16_le(uint8_t *buffer, unsigned int count,
+ float scaler)
+{
+ int i;
+ int16_t *out = (int16_t *)buffer;
+
+ if (scaler > MAX_VOLUME_TO_SCALE)
+ return;
+
+ if (scaler < MIN_VOLUME_TO_SCALE) {
+ memset(out, 0, count * sizeof(*out));
+ return;
+ }
+
+ for (i = 0; i < count; i++)
+ out[i] *= scaler;
+}
+
+static void cras_mix_add_s16_le(uint8_t *dst, uint8_t *src,
+ unsigned int count, unsigned int index,
+ int mute, float mix_vol)
+{
+ int16_t *out = (int16_t *)dst;
+ int16_t *in = (int16_t *)src;
+
+ if (mute || (mix_vol < MIN_VOLUME_TO_SCALE)) {
+ if (index == 0)
+ memset(out, 0, count * sizeof(*out));
+ return;
+ }
+
+ if (index == 0)
+ return copy_scaled_s16_le(out, in, count, mix_vol);
+
+ scale_add_clip_s16_le(out, in, count, mix_vol);
+}
+
+static void cras_mix_add_scale_stride_s16_le(uint8_t *dst, uint8_t *src,
+ unsigned int dst_stride,
+ unsigned int src_stride,
+ unsigned int count,
+ float scaler)
+{
+ unsigned int i;
+
+ /* optimise the loops for vectorization */
+ if (dst_stride == src_stride && dst_stride == 2) {
+
+ for (i = 0; i < count; i++) {
+ int32_t sum;
+ if (need_to_scale(scaler))
+ sum = *(int16_t *)dst +
+ *(int16_t *)src * scaler;
+ else
+ sum = *(int16_t *)dst + *(int16_t *)src;
+ if (sum > INT16_MAX)
+ sum = INT16_MAX;
+ else if (sum < INT16_MIN)
+ sum = INT16_MIN;
+ *(int16_t*)dst = sum;
+ dst += 2;
+ src += 2;
+ }
+ } else if (dst_stride == src_stride && dst_stride == 4) {
+
+ for (i = 0; i < count; i++) {
+ int32_t sum;
+ if (need_to_scale(scaler))
+ sum = *(int16_t *)dst +
+ *(int16_t *)src * scaler;
+ else
+ sum = *(int16_t *)dst + *(int16_t *)src;
+ if (sum > INT16_MAX)
+ sum = INT16_MAX;
+ else if (sum < INT16_MIN)
+ sum = INT16_MIN;
+ *(int16_t*)dst = sum;
+ dst += 4;
+ src += 4;
+ }
+ } else {
+ for (i = 0; i < count; i++) {
+ int32_t sum;
+ if (need_to_scale(scaler))
+ sum = *(int16_t *)dst +
+ *(int16_t *)src * scaler;
+ else
+ sum = *(int16_t *)dst + *(int16_t *)src;
+ if (sum > INT16_MAX)
+ sum = INT16_MAX;
+ else if (sum < INT16_MIN)
+ sum = INT16_MIN;
+ *(int16_t*)dst = sum;
+ dst += dst_stride;
+ src += src_stride;
+ }
+ }
+}
+
+/*
+ * Signed 24 bit little endian functions.
+ */
+
+static void cras_mix_add_clip_s24_le(int32_t *dst,
+ const int32_t *src,
+ size_t count)
+{
+ int32_t sum;
+ size_t i;
+
+ for (i = 0; i < count; i++) {
+ sum = dst[i] + src[i];
+ if (sum > 0x007fffff)
+ sum = 0x007fffff;
+ else if (sum < (int32_t)0xff800000)
+ sum = (int32_t)0xff800000;
+ dst[i] = sum;
+ }
+}
+
+/* Adds src into dst, after scaling by vol.
+ * Just hard limits to the min and max S24 value, can be improved later. */
+static void scale_add_clip_s24_le(int32_t *dst,
+ const int32_t *src,
+ size_t count,
+ float vol)
+{
+ int32_t sum;
+ size_t i;
+
+ if (vol > MAX_VOLUME_TO_SCALE)
+ return cras_mix_add_clip_s24_le(dst, src, count);
+
+ for (i = 0; i < count; i++) {
+ sum = dst[i] + (int32_t)(src[i] * vol);
+ if (sum > 0x007fffff)
+ sum = 0x007fffff;
+ else if (sum < (int32_t)0xff800000)
+ sum = (int32_t)0xff800000;
+ dst[i] = sum;
+ }
+}
+
+/* Adds the first stream to the mix. Don't need to mix, just setup to the new
+ * values. If volume is 1.0, just memcpy. */
+static void copy_scaled_s24_le(int32_t *dst,
+ const int32_t *src,
+ size_t count,
+ float volume_scaler)
+{
+ int i;
+
+ if (volume_scaler > MAX_VOLUME_TO_SCALE) {
+ memcpy(dst, src, count * sizeof(*src));
+ return;
+ }
+
+ for (i = 0; i < count; i++)
+ dst[i] = src[i] * volume_scaler;
+}
+
+static void cras_scale_buffer_inc_s24_le(uint8_t *buffer, unsigned int count,
+ float scaler, float increment, int step)
+{
+ int i = 0, j;
+ int32_t *out = (int32_t *)buffer;
+
+ if (scaler > MAX_VOLUME_TO_SCALE && increment > 0)
+ return;
+
+ if (scaler < MIN_VOLUME_TO_SCALE && increment < 0) {
+ memset(out, 0, count * sizeof(*out));
+ return;
+ }
+
+ while (i + step <= count) {
+ for (j = 0; j < step; j++) {
+ if (scaler > MAX_VOLUME_TO_SCALE) {
+ } else if (scaler < MIN_VOLUME_TO_SCALE) {
+ out[i] = 0;
+ } else {
+ out[i] *= scaler;
+ }
+ i++;
+ }
+ scaler += increment;
+ }
+}
+
+static void cras_scale_buffer_s24_le(uint8_t *buffer, unsigned int count,
+ float scaler)
+{
+ int i;
+ int32_t *out = (int32_t *)buffer;
+
+ if (scaler > MAX_VOLUME_TO_SCALE)
+ return;
+
+ if (scaler < MIN_VOLUME_TO_SCALE) {
+ memset(out, 0, count * sizeof(*out));
+ return;
+ }
+
+ for (i = 0; i < count; i++)
+ out[i] *= scaler;
+}
+
+static void cras_mix_add_s24_le(uint8_t *dst, uint8_t *src,
+ unsigned int count, unsigned int index,
+ int mute, float mix_vol)
+{
+ int32_t *out = (int32_t *)dst;
+ int32_t *in = (int32_t *)src;
+
+ if (mute || (mix_vol < MIN_VOLUME_TO_SCALE)) {
+ if (index == 0)
+ memset(out, 0, count * sizeof(*out));
+ return;
+ }
+
+ if (index == 0)
+ return copy_scaled_s24_le(out, in, count, mix_vol);
+
+ scale_add_clip_s24_le(out, in, count, mix_vol);
+}
+
+static void cras_mix_add_scale_stride_s24_le(uint8_t *dst, uint8_t *src,
+ unsigned int dst_stride,
+ unsigned int src_stride,
+ unsigned int count,
+ float scaler)
+{
+ unsigned int i;
+
+ /* optimise the loops for vectorization */
+ if (dst_stride == src_stride && dst_stride == 4) {
+
+ for (i = 0; i < count; i++) {
+ int32_t sum;
+ if (need_to_scale(scaler))
+ sum = *(int32_t *)dst +
+ *(int32_t *)src * scaler;
+ else
+ sum = *(int32_t *)dst + *(int32_t *)src;
+ if (sum > 0x007fffff)
+ sum = 0x007fffff;
+ else if (sum < (int32_t)0xff800000)
+ sum = (int32_t)0xff800000;
+ *(int32_t*)dst = sum;
+ dst += 4;
+ src += 4;
+ }
+ } else {
+
+ for (i = 0; i < count; i++) {
+ int32_t sum;
+ if (need_to_scale(scaler))
+ sum = *(int32_t *)dst +
+ *(int32_t *)src * scaler;
+ else
+ sum = *(int32_t *)dst + *(int32_t *)src;
+ if (sum > 0x007fffff)
+ sum = 0x007fffff;
+ else if (sum < (int32_t)0xff800000)
+ sum = (int32_t)0xff800000;
+ *(int32_t*)dst = sum;
+ dst += dst_stride;
+ src += src_stride;
+ }
+ }
+}
+
+/*
+ * Signed 32 bit little endian functions.
+ */
+
+static void cras_mix_add_clip_s32_le(int32_t *dst,
+ const int32_t *src,
+ size_t count)
+{
+ int64_t sum;
+ size_t i;
+
+ for (i = 0; i < count; i++) {
+ sum = (int64_t)dst[i] + (int64_t)src[i];
+ if (sum > INT32_MAX)
+ sum = INT32_MAX;
+ else if (sum < INT32_MIN)
+ sum = INT32_MIN;
+ dst[i] = sum;
+ }
+}
+
+/* Adds src into dst, after scaling by vol.
+ * Just hard limits to the min and max S32 value, can be improved later. */
+static void scale_add_clip_s32_le(int32_t *dst,
+ const int32_t *src,
+ size_t count,
+ float vol)
+{
+ int64_t sum;
+ size_t i;
+
+ if (vol > MAX_VOLUME_TO_SCALE)
+ return cras_mix_add_clip_s32_le(dst, src, count);
+
+ for (i = 0; i < count; i++) {
+ sum = (int64_t)dst[i] + (int64_t)(src[i] * vol);
+ if (sum > INT32_MAX)
+ sum = INT32_MAX;
+ else if (sum < INT32_MIN)
+ sum = INT32_MIN;
+ dst[i] = sum;
+ }
+}
+
+/* Adds the first stream to the mix. Don't need to mix, just setup to the new
+ * values. If volume is 1.0, just memcpy. */
+static void copy_scaled_s32_le(int32_t *dst,
+ const int32_t *src,
+ size_t count,
+ float volume_scaler)
+{
+ int i;
+
+ if (volume_scaler > MAX_VOLUME_TO_SCALE) {
+ memcpy(dst, src, count * sizeof(*src));
+ return;
+ }
+
+ for (i = 0; i < count; i++)
+ dst[i] = src[i] * volume_scaler;
+}
+
+static void cras_scale_buffer_inc_s32_le(uint8_t *buffer, unsigned int count,
+ float scaler, float increment, int step)
+{
+ int i = 0, j;
+ int32_t *out = (int32_t *)buffer;
+
+ if (scaler > MAX_VOLUME_TO_SCALE && increment > 0)
+ return;
+
+ if (scaler < MIN_VOLUME_TO_SCALE && increment < 0) {
+ memset(out, 0, count * sizeof(*out));
+ return;
+ }
+
+ while (i + step <= count) {
+ for (j = 0; j < step; j++) {
+ if (scaler > MAX_VOLUME_TO_SCALE) {
+ } else if (scaler < MIN_VOLUME_TO_SCALE) {
+ out[i] = 0;
+ } else {
+ out[i] *= scaler;
+ }
+ i++;
+ }
+ scaler += increment;
+ }
+}
+
+static void cras_scale_buffer_s32_le(uint8_t *buffer, unsigned int count,
+ float scaler)
+{
+ int i;
+ int32_t *out = (int32_t *)buffer;
+
+ if (scaler > MAX_VOLUME_TO_SCALE)
+ return;
+
+ if (scaler < MIN_VOLUME_TO_SCALE) {
+ memset(out, 0, count * sizeof(*out));
+ return;
+ }
+
+ for (i = 0; i < count; i++)
+ out[i] *= scaler;
+}
+
+static void cras_mix_add_s32_le(uint8_t *dst, uint8_t *src,
+ unsigned int count, unsigned int index,
+ int mute, float mix_vol)
+{
+ int32_t *out = (int32_t *)dst;
+ int32_t *in = (int32_t *)src;
+
+ if (mute || (mix_vol < MIN_VOLUME_TO_SCALE)) {
+ if (index == 0)
+ memset(out, 0, count * sizeof(*out));
+ return;
+ }
+
+ if (index == 0)
+ return copy_scaled_s32_le(out, in, count, mix_vol);
+
+ scale_add_clip_s32_le(out, in, count, mix_vol);
+}
+
+static void cras_mix_add_scale_stride_s32_le(uint8_t *dst, uint8_t *src,
+ unsigned int dst_stride,
+ unsigned int src_stride,
+ unsigned int count,
+ float scaler)
+{
+ unsigned int i;
+
+ /* optimise the loops for vectorization */
+ if (dst_stride == src_stride && dst_stride == 4) {
+
+ for (i = 0; i < count; i++) {
+ int64_t sum;
+ if (need_to_scale(scaler))
+ sum = *(int32_t *)dst +
+ *(int32_t *)src * scaler;
+ else
+ sum = *(int32_t *)dst + *(int32_t *)src;
+ if (sum > INT32_MAX)
+ sum = INT32_MAX;
+ else if (sum < INT32_MIN)
+ sum = INT32_MIN;
+ *(int32_t*)dst = sum;
+ dst += 4;
+ src += 4;
+ }
+ } else {
+
+ for (i = 0; i < count; i++) {
+ int64_t sum;
+ if (need_to_scale(scaler))
+ sum = *(int32_t *)dst +
+ *(int32_t *)src * scaler;
+ else
+ sum = *(int32_t *)dst + *(int32_t *)src;
+ if (sum > INT32_MAX)
+ sum = INT32_MAX;
+ else if (sum < INT32_MIN)
+ sum = INT32_MIN;
+ *(int32_t*)dst = sum;
+ dst += dst_stride;
+ src += src_stride;
+ }
+ }
+}
+
+/*
+ * Signed 24 bit little endian in three bytes functions.
+ */
+
+/* Convert 3bytes Signed 24bit integer to a Signed 32bit integer.
+ * Just a helper function. */
+static inline void convert_single_s243le_to_s32le(int32_t *dst,
+ const uint8_t *src)
+{
+ *dst = 0;
+ memcpy((uint8_t *)dst + 1, src, 3);
+}
+
+static inline void convert_single_s32le_to_s243le(uint8_t *dst,
+ const int32_t *src)
+{
+ memcpy(dst, (uint8_t *)src + 1, 3);
+}
+
+static void cras_mix_add_clip_s24_3le(uint8_t *dst,
+ const uint8_t *src,
+ size_t count)
+{
+ int64_t sum;
+ int32_t dst_frame;
+ int32_t src_frame;
+ size_t i;
+
+ for (i = 0; i < count; i++, dst += 3, src += 3) {
+ convert_single_s243le_to_s32le(&dst_frame, dst);
+ convert_single_s243le_to_s32le(&src_frame, src);
+ sum = (int64_t)dst_frame + (int64_t)src_frame;
+ if (sum > INT32_MAX)
+ sum = INT32_MAX;
+ else if (sum < INT32_MIN)
+ sum = INT32_MIN;
+ dst_frame = (int32_t)sum;
+ convert_single_s32le_to_s243le(dst, &dst_frame);
+ }
+}
+
+/* Adds src into dst, after scaling by vol.
+ * Just hard limits to the min and max S24 value, can be improved later. */
+static void scale_add_clip_s24_3le(uint8_t *dst,
+ const uint8_t *src,
+ size_t count,
+ float vol)
+{
+ int64_t sum;
+ int32_t dst_frame;
+ int32_t src_frame;
+ size_t i;
+
+ if (vol > MAX_VOLUME_TO_SCALE)
+ return cras_mix_add_clip_s24_3le(dst, src, count);
+
+ for (i = 0; i < count; i++, dst += 3, src += 3) {
+ convert_single_s243le_to_s32le(&dst_frame, dst);
+ convert_single_s243le_to_s32le(&src_frame, src);
+ sum = (int64_t)dst_frame + (int64_t)(src_frame * vol);
+ if (sum > INT32_MAX)
+ sum = INT32_MAX;
+ else if (sum < INT32_MIN)
+ sum = INT32_MIN;
+ dst_frame = (int32_t)sum;
+ convert_single_s32le_to_s243le(dst, &dst_frame);
+ }
+}
+
+/* Adds the first stream to the mix. Don't need to mix, just setup to the new
+ * values. If volume is 1.0, just memcpy. */
+static void copy_scaled_s24_3le(uint8_t *dst,
+ const uint8_t *src,
+ size_t count,
+ float volume_scaler)
+{
+ int32_t frame;
+ size_t i;
+
+ if (volume_scaler > MAX_VOLUME_TO_SCALE) {
+ memcpy(dst, src, 3 * count * sizeof(*src));
+ return;
+ }
+
+ for (i = 0; i < count; i++, dst += 3, src += 3) {
+ convert_single_s243le_to_s32le(&frame, src);
+ frame *= volume_scaler;
+ convert_single_s32le_to_s243le(dst, &frame);
+ }
+}
+
+static void cras_scale_buffer_inc_s24_3le(uint8_t *buffer, unsigned int count,
+ float scaler, float increment, int step)
+{
+ int32_t frame;
+ int i = 0, j;
+
+ if (scaler > MAX_VOLUME_TO_SCALE && increment > 0)
+ return;
+
+ if (scaler < MIN_VOLUME_TO_SCALE && increment < 0) {
+ memset(buffer, 0, 3 * count * sizeof(*buffer));
+ return;
+ }
+
+ while (i + step <= count) {
+ for (j = 0; j < step; j++) {
+ convert_single_s243le_to_s32le(&frame, buffer);
+
+ if (scaler > MAX_VOLUME_TO_SCALE) {
+ } else if (scaler < MIN_VOLUME_TO_SCALE) {
+ frame = 0;
+ } else {
+ frame *= scaler;
+ }
+
+ convert_single_s32le_to_s243le(buffer, &frame);
+
+ i++;
+ buffer += 3;
+ }
+ scaler += increment;
+ }
+}
+
+static void cras_scale_buffer_s24_3le(uint8_t *buffer, unsigned int count,
+ float scaler)
+{
+ int32_t frame;
+ int i;
+
+ if (scaler > MAX_VOLUME_TO_SCALE)
+ return;
+
+ if (scaler < MIN_VOLUME_TO_SCALE) {
+ memset(buffer, 0, 3 * count * sizeof(*buffer));
+ return;
+ }
+
+ for (i = 0; i < count; i++, buffer += 3) {
+ convert_single_s243le_to_s32le(&frame, buffer);
+ frame *= scaler;
+ convert_single_s32le_to_s243le(buffer, &frame);
+ }
+}
+
+static void cras_mix_add_s24_3le(uint8_t *dst, uint8_t *src,
+ unsigned int count, unsigned int index,
+ int mute, float mix_vol)
+{
+ uint8_t *out = dst;
+ uint8_t *in = src;
+
+ if (mute || (mix_vol < MIN_VOLUME_TO_SCALE)) {
+ if (index == 0)
+ memset(out, 0, 3 * count * sizeof(*out));
+ return;
+ }
+
+ if (index == 0)
+ return copy_scaled_s24_3le(out, in, count, mix_vol);
+
+ scale_add_clip_s24_3le(out, in, count, mix_vol);
+}
+
+static void cras_mix_add_scale_stride_s24_3le(uint8_t *dst, uint8_t *src,
+ unsigned int dst_stride,
+ unsigned int src_stride,
+ unsigned int count,
+ float scaler)
+{
+ unsigned int i;
+ int64_t sum;
+ int32_t dst_frame;
+ int32_t src_frame;
+
+ for (i = 0; i < count; i++) {
+ convert_single_s243le_to_s32le(&dst_frame, dst);
+ convert_single_s243le_to_s32le(&src_frame, src);
+ if (need_to_scale(scaler))
+ sum = (int64_t)dst_frame + (int64_t)src_frame * scaler;
+ else
+ sum = (int64_t)dst_frame + (int64_t)src_frame;
+ if (sum > INT32_MAX)
+ sum = INT32_MAX;
+ else if (sum < INT32_MIN)
+ sum = INT32_MIN;
+ dst_frame = (int32_t)sum;
+ convert_single_s32le_to_s243le(dst, &dst_frame);
+ dst += dst_stride;
+ src += src_stride;
+ }
+}
+
+static void scale_buffer_increment(snd_pcm_format_t fmt, uint8_t *buff,
+ unsigned int count, float scaler,
+ float increment, int step)
+{
+ switch (fmt) {
+ case SND_PCM_FORMAT_S16_LE:
+ return cras_scale_buffer_inc_s16_le(buff, count, scaler,
+ increment, step);
+ case SND_PCM_FORMAT_S24_LE:
+ return cras_scale_buffer_inc_s24_le(buff, count, scaler,
+ increment, step);
+ case SND_PCM_FORMAT_S32_LE:
+ return cras_scale_buffer_inc_s32_le(buff, count, scaler,
+ increment, step);
+ case SND_PCM_FORMAT_S24_3LE:
+ return cras_scale_buffer_inc_s24_3le(buff, count, scaler,
+ increment, step);
+ default:
+ break;
+ }
+}
+
+static void scale_buffer(snd_pcm_format_t fmt, uint8_t *buff, unsigned int count,
+ float scaler)
+{
+ switch (fmt) {
+ case SND_PCM_FORMAT_S16_LE:
+ return cras_scale_buffer_s16_le(buff, count, scaler);
+ case SND_PCM_FORMAT_S24_LE:
+ return cras_scale_buffer_s24_le(buff, count, scaler);
+ case SND_PCM_FORMAT_S32_LE:
+ return cras_scale_buffer_s32_le(buff, count, scaler);
+ case SND_PCM_FORMAT_S24_3LE:
+ return cras_scale_buffer_s24_3le(buff, count, scaler);
+ default:
+ break;
+ }
+}
+
+static void mix_add(snd_pcm_format_t fmt, uint8_t *dst, uint8_t *src,
+ unsigned int count, unsigned int index,
+ int mute, float mix_vol)
+{
+ switch (fmt) {
+ case SND_PCM_FORMAT_S16_LE:
+ return cras_mix_add_s16_le(dst, src, count, index, mute,
+ mix_vol);
+ case SND_PCM_FORMAT_S24_LE:
+ return cras_mix_add_s24_le(dst, src, count, index, mute,
+ mix_vol);
+ case SND_PCM_FORMAT_S32_LE:
+ return cras_mix_add_s32_le(dst, src, count, index, mute,
+ mix_vol);
+ case SND_PCM_FORMAT_S24_3LE:
+ return cras_mix_add_s24_3le(dst, src, count, index, mute,
+ mix_vol);
+ default:
+ break;
+ }
+}
+
+static void mix_add_scale_stride(snd_pcm_format_t fmt, uint8_t *dst,
+ uint8_t *src, unsigned int count,
+ unsigned int dst_stride, unsigned int src_stride,
+ float scaler)
+{
+ switch (fmt) {
+ case SND_PCM_FORMAT_S16_LE:
+ return cras_mix_add_scale_stride_s16_le(dst, src, dst_stride,
+ src_stride, count, scaler);
+ case SND_PCM_FORMAT_S24_LE:
+ return cras_mix_add_scale_stride_s24_le(dst, src, dst_stride,
+ src_stride, count, scaler);
+ case SND_PCM_FORMAT_S32_LE:
+ return cras_mix_add_scale_stride_s32_le(dst, src, dst_stride,
+ src_stride, count, scaler);
+ case SND_PCM_FORMAT_S24_3LE:
+ return cras_mix_add_scale_stride_s24_3le(dst, src, dst_stride,
+ src_stride, count, scaler);
+ default:
+ break;
+ }
+}
+
+static size_t mix_mute_buffer(uint8_t *dst,
+ size_t frame_bytes,
+ size_t count)
+{
+ memset(dst, 0, count * frame_bytes);
+ return count;
+}
+
+const struct cras_mix_ops OPS(mixer_ops) = {
+ .scale_buffer = scale_buffer,
+ .scale_buffer_increment = scale_buffer_increment,
+ .add = mix_add,
+ .add_scale_stride = mix_add_scale_stride,
+ .mute_buffer = mix_mute_buffer,
+};
diff --git a/cras/src/server/cras_mix_ops.h b/cras/src/server/cras_mix_ops.h
new file mode 100644
index 00000000..8246a07f
--- /dev/null
+++ b/cras/src/server/cras_mix_ops.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2012 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_MIX_OPS_H_
+#define CRAS_MIX_OPS_H_
+
+#include <stdint.h>
+
+#include "cras_system_state.h"
+
+extern const struct cras_mix_ops mixer_ops;
+extern const struct cras_mix_ops mixer_ops_sse42;
+extern const struct cras_mix_ops mixer_ops_avx;
+extern const struct cras_mix_ops mixer_ops_avx2;
+extern const struct cras_mix_ops mixer_ops_fma;
+
+/* Struct containing ops to implement mix/scale on a buffer of samples.
+ * Different architecture can provide different implementations and wraps
+ * the implementations into cras_mix_ops.
+ * Different sample formats will be handled by different implementations.
+ * The usage of each operation is explained in cras_mix.h
+ *
+ * Members:
+ * scale_buffer_increment: See cras_scale_buffer_increment.
+ * scale_buffer: See cras_scale_buffer.
+ * add: See cras_mix_add.
+ * add_scale_stride: See cras_mix_add_scale_stride.
+ * mute_buffer: cras_mix_mute_buffer.
+ */
+struct cras_mix_ops {
+ void (*scale_buffer_increment)(snd_pcm_format_t fmt, uint8_t *buff,
+ unsigned int count, float scaler,
+ float increment, int step);
+ void (*scale_buffer)(snd_pcm_format_t fmt, uint8_t *buff,
+ unsigned int count, float scaler);
+ void (*add)(snd_pcm_format_t fmt, uint8_t *dst, uint8_t *src,
+ unsigned int count, unsigned int index,
+ int mute, float mix_vol);
+ void (*add_scale_stride)(snd_pcm_format_t fmt, uint8_t *dst,
+ uint8_t *src, unsigned int count,
+ unsigned int dst_stride, unsigned int src_stride,
+ float scaler);
+ size_t (*mute_buffer)(uint8_t *dst,
+ size_t frame_bytes,
+ size_t count);
+};
+#endif
diff --git a/cras/src/server/cras_observer.c b/cras/src/server/cras_observer.c
new file mode 100644
index 00000000..d0366d59
--- /dev/null
+++ b/cras/src/server/cras_observer.c
@@ -0,0 +1,507 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "cras_observer.h"
+
+#include "cras_alert.h"
+#include "cras_iodev_list.h"
+#include "utlist.h"
+
+struct cras_observer_client {
+ struct cras_observer_ops ops;
+ void *context;
+ struct cras_observer_client *next, *prev;
+};
+
+struct cras_observer_alerts {
+ struct cras_alert *output_volume;
+ struct cras_alert *output_mute;
+ struct cras_alert *capture_gain;
+ struct cras_alert *capture_mute;
+ struct cras_alert *nodes;
+ struct cras_alert *active_node;
+ struct cras_alert *output_node_volume;
+ struct cras_alert *node_left_right_swapped;
+ struct cras_alert *input_node_gain;
+ struct cras_alert *suspend_changed;
+ /* If all events for active streams went through a single alert then
+ * we might miss some because the alert code does not send every
+ * alert message. To ensure that the event sent contains the correct
+ * number of active streams per direction, make the alerts
+ * per-direciton. */
+ struct cras_alert *num_active_streams[CRAS_NUM_DIRECTIONS];
+};
+
+struct cras_observer_server {
+ struct cras_observer_alerts alerts;
+ struct cras_observer_client *clients;
+};
+
+struct cras_observer_alert_data_volume {
+ int32_t volume;
+};
+
+struct cras_observer_alert_data_mute {
+ int muted;
+ int user_muted;
+ int mute_locked;
+};
+
+struct cras_observer_alert_data_active_node {
+ enum CRAS_STREAM_DIRECTION direction;
+ cras_node_id_t node_id;
+};
+
+struct cras_observer_alert_data_node_volume {
+ cras_node_id_t node_id;
+ int32_t volume;
+};
+
+struct cras_observer_alert_data_node_lr_swapped {
+ cras_node_id_t node_id;
+ int swapped;
+};
+
+struct cras_observer_alert_data_suspend {
+ int suspended;
+};
+
+struct cras_observer_alert_data_streams {
+ enum CRAS_STREAM_DIRECTION direction;
+ uint32_t num_active_streams;
+};
+
+/* Global observer instance. */
+static struct cras_observer_server *g_observer;
+
+/* Empty observer ops. */
+static struct cras_observer_ops g_empty_ops;
+
+/*
+ * Alert handlers for delayed callbacks.
+ */
+
+static void output_volume_alert(void *arg, void *data)
+{
+ struct cras_observer_client *client;
+ struct cras_observer_alert_data_volume *volume_data =
+ (struct cras_observer_alert_data_volume *)data;
+
+ DL_FOREACH(g_observer->clients, client) {
+ if (client->ops.output_volume_changed)
+ client->ops.output_volume_changed(
+ client->context,
+ volume_data->volume);
+ }
+}
+
+static void output_mute_alert(void *arg, void *data)
+{
+ struct cras_observer_client *client;
+ struct cras_observer_alert_data_mute *mute_data =
+ (struct cras_observer_alert_data_mute *)data;
+
+ DL_FOREACH(g_observer->clients, client) {
+ if (client->ops.output_mute_changed)
+ client->ops.output_mute_changed(
+ client->context,
+ mute_data->muted,
+ mute_data->user_muted,
+ mute_data->mute_locked);
+ }
+}
+
+static void capture_gain_alert(void *arg, void *data)
+{
+ struct cras_observer_client *client;
+ struct cras_observer_alert_data_volume *volume_data =
+ (struct cras_observer_alert_data_volume *)data;
+
+ DL_FOREACH(g_observer->clients, client) {
+ if (client->ops.capture_gain_changed)
+ client->ops.capture_gain_changed(
+ client->context,
+ volume_data->volume);
+ }
+}
+
+static void capture_mute_alert(void *arg, void *data)
+{
+ struct cras_observer_client *client;
+ struct cras_observer_alert_data_mute *mute_data =
+ (struct cras_observer_alert_data_mute *)data;
+
+ DL_FOREACH(g_observer->clients, client) {
+ if (client->ops.capture_mute_changed)
+ client->ops.capture_mute_changed(
+ client->context,
+ mute_data->muted,
+ mute_data->mute_locked);
+ }
+}
+
+static void nodes_prepare(struct cras_alert *alert)
+{
+ cras_iodev_list_update_device_list();
+}
+
+static void nodes_alert(void *arg, void *data)
+{
+ struct cras_observer_client *client;
+
+ DL_FOREACH(g_observer->clients, client) {
+ if (client->ops.nodes_changed)
+ client->ops.nodes_changed(client->context);
+ }
+}
+
+static void active_node_alert(void *arg, void *data)
+{
+ struct cras_observer_client *client;
+ struct cras_observer_alert_data_active_node *node_data =
+ (struct cras_observer_alert_data_active_node *)data;
+
+ DL_FOREACH(g_observer->clients, client) {
+ if (client->ops.active_node_changed)
+ client->ops.active_node_changed(
+ client->context,
+ node_data->direction,
+ node_data->node_id);
+ }
+}
+
+static void output_node_volume_alert(void *arg, void *data)
+{
+ struct cras_observer_client *client;
+ struct cras_observer_alert_data_node_volume *node_data =
+ (struct cras_observer_alert_data_node_volume *)data;
+
+ DL_FOREACH(g_observer->clients, client) {
+ if (client->ops.output_node_volume_changed)
+ client->ops.output_node_volume_changed(
+ client->context,
+ node_data->node_id,
+ node_data->volume);
+ }
+}
+
+static void node_left_right_swapped_alert(void *arg, void *data)
+{
+ struct cras_observer_client *client;
+ struct cras_observer_alert_data_node_lr_swapped *node_data =
+ (struct cras_observer_alert_data_node_lr_swapped *)data;
+
+ DL_FOREACH(g_observer->clients, client) {
+ if (client->ops.node_left_right_swapped_changed)
+ client->ops.node_left_right_swapped_changed(
+ client->context,
+ node_data->node_id,
+ node_data->swapped);
+ }
+}
+
+static void input_node_gain_alert(void *arg, void *data)
+{
+ struct cras_observer_client *client;
+ struct cras_observer_alert_data_node_volume *node_data =
+ (struct cras_observer_alert_data_node_volume *)data;
+
+ DL_FOREACH(g_observer->clients, client) {
+ if (client->ops.input_node_gain_changed)
+ client->ops.input_node_gain_changed(
+ client->context,
+ node_data->node_id,
+ node_data->volume);
+ }
+}
+
+static void suspend_changed_alert(void *arg, void *data)
+{
+ struct cras_observer_client *client;
+ struct cras_observer_alert_data_suspend *suspend_data =
+ (struct cras_observer_alert_data_suspend *)data;
+
+ DL_FOREACH(g_observer->clients, client) {
+ if (client->ops.suspend_changed)
+ client->ops.suspend_changed(
+ client->context,
+ suspend_data->suspended);
+ }
+}
+
+static void num_active_streams_alert(void *arg, void *data)
+{
+ struct cras_observer_client *client;
+ struct cras_observer_alert_data_streams *streams_data =
+ (struct cras_observer_alert_data_streams *)data;
+
+ DL_FOREACH(g_observer->clients, client) {
+ if (client->ops.num_active_streams_changed)
+ client->ops.num_active_streams_changed(
+ client->context,
+ streams_data->direction,
+ streams_data->num_active_streams);
+ }
+}
+
+static int cras_observer_server_set_alert(struct cras_alert **alert,
+ cras_alert_cb cb,
+ cras_alert_prepare prepare,
+ unsigned int flags)
+{
+ *alert = cras_alert_create(prepare, flags);
+ if (!*alert)
+ return -ENOMEM;
+ return cras_alert_add_callback(*alert, cb, NULL);
+}
+
+#define CRAS_OBSERVER_SET_ALERT(alert,prepare,flags) \
+ do { \
+ rc = cras_observer_server_set_alert( \
+ &g_observer->alerts.alert, alert##_alert, \
+ prepare, flags); \
+ if (rc) \
+ goto error; \
+ } while(0)
+
+#define CRAS_OBSERVER_SET_ALERT_WITH_DIRECTION(alert,direction) \
+ do { \
+ rc = cras_observer_server_set_alert( \
+ &g_observer->alerts.alert[direction], \
+ alert##_alert, NULL, 0); \
+ if (rc) \
+ goto error; \
+ } while(0)
+
+/*
+ * Public interface
+ */
+
+int cras_observer_server_init()
+{
+ int rc;
+
+ memset(&g_empty_ops, 0, sizeof(g_empty_ops));
+ g_observer = (struct cras_observer_server *)
+ calloc(1, sizeof(struct cras_observer_server));
+ if (!g_observer)
+ return -ENOMEM;
+
+ CRAS_OBSERVER_SET_ALERT(output_volume, NULL, 0);
+ CRAS_OBSERVER_SET_ALERT(output_mute, NULL, 0);
+ CRAS_OBSERVER_SET_ALERT(capture_gain, NULL, 0);
+ CRAS_OBSERVER_SET_ALERT(capture_mute, NULL, 0);
+ CRAS_OBSERVER_SET_ALERT(nodes, nodes_prepare, 0);
+ CRAS_OBSERVER_SET_ALERT(active_node, nodes_prepare,
+ CRAS_ALERT_FLAG_KEEP_ALL_DATA);
+ CRAS_OBSERVER_SET_ALERT(output_node_volume, NULL, 0);
+ CRAS_OBSERVER_SET_ALERT(node_left_right_swapped, NULL, 0);
+ CRAS_OBSERVER_SET_ALERT(input_node_gain, NULL, 0);
+ CRAS_OBSERVER_SET_ALERT(suspend_changed, NULL, 0);
+
+ CRAS_OBSERVER_SET_ALERT_WITH_DIRECTION(
+ num_active_streams, CRAS_STREAM_OUTPUT);
+ CRAS_OBSERVER_SET_ALERT_WITH_DIRECTION(
+ num_active_streams, CRAS_STREAM_INPUT);
+ CRAS_OBSERVER_SET_ALERT_WITH_DIRECTION(
+ num_active_streams, CRAS_STREAM_POST_MIX_PRE_DSP);
+ return 0;
+
+error:
+ cras_observer_server_free();
+ return rc;
+}
+
+void cras_observer_server_free()
+{
+ if (!g_observer)
+ return;
+ cras_alert_destroy(g_observer->alerts.output_volume);
+ cras_alert_destroy(g_observer->alerts.output_mute);
+ cras_alert_destroy(g_observer->alerts.capture_gain);
+ cras_alert_destroy(g_observer->alerts.capture_mute);
+ cras_alert_destroy(g_observer->alerts.nodes);
+ cras_alert_destroy(g_observer->alerts.active_node);
+ cras_alert_destroy(g_observer->alerts.output_node_volume);
+ cras_alert_destroy(g_observer->alerts.node_left_right_swapped);
+ cras_alert_destroy(g_observer->alerts.input_node_gain);
+ cras_alert_destroy(g_observer->alerts.suspend_changed);
+ cras_alert_destroy(g_observer->alerts.num_active_streams[
+ CRAS_STREAM_OUTPUT]);
+ cras_alert_destroy(g_observer->alerts.num_active_streams[
+ CRAS_STREAM_INPUT]);
+ cras_alert_destroy(g_observer->alerts.num_active_streams[
+ CRAS_STREAM_POST_MIX_PRE_DSP]);
+ free(g_observer);
+ g_observer = NULL;
+}
+
+int cras_observer_ops_are_empty(const struct cras_observer_ops *ops)
+{
+ return memcmp(ops, &g_empty_ops, sizeof(*ops)) == 0;
+}
+
+void cras_observer_get_ops(const struct cras_observer_client *client,
+ struct cras_observer_ops *ops)
+{
+ if (!ops)
+ return;
+ if (!client)
+ memset(ops, 0, sizeof(*ops));
+ else
+ memcpy(ops, &client->ops, sizeof(*ops));
+}
+
+void cras_observer_set_ops(struct cras_observer_client *client,
+ const struct cras_observer_ops *ops)
+{
+ if (!client)
+ return;
+ if (!ops)
+ memset(&client->ops, 0, sizeof(client->ops));
+ else
+ memcpy(&client->ops, ops, sizeof(client->ops));
+}
+
+struct cras_observer_client *cras_observer_add(
+ const struct cras_observer_ops *ops,
+ void *context)
+{
+ struct cras_observer_client *client;
+
+ client = (struct cras_observer_client *)calloc(1, sizeof(*client));
+ if (!client)
+ return NULL;
+ client->context = context;
+ DL_APPEND(g_observer->clients, client);
+ cras_observer_set_ops(client, ops);
+ return client;
+}
+
+void cras_observer_remove(struct cras_observer_client *client)
+{
+ if (!client)
+ return;
+ DL_DELETE(g_observer->clients, client);
+ free(client);
+}
+
+/*
+ * Public interface for notifiers.
+ */
+
+void cras_observer_notify_output_volume(int32_t volume)
+{
+ struct cras_observer_alert_data_volume data;
+
+ data.volume = volume;
+ cras_alert_pending_data(g_observer->alerts.output_volume,
+ &data, sizeof(data));
+}
+
+void cras_observer_notify_output_mute(int muted, int user_muted,
+ int mute_locked)
+{
+ struct cras_observer_alert_data_mute data;
+
+ data.muted = muted;
+ data.user_muted = user_muted;
+ data.mute_locked = mute_locked;
+ cras_alert_pending_data(g_observer->alerts.output_mute,
+ &data, sizeof(data));
+}
+
+void cras_observer_notify_capture_gain(int32_t gain)
+{
+ struct cras_observer_alert_data_volume data;
+
+ data.volume = gain;
+ cras_alert_pending_data(g_observer->alerts.capture_gain,
+ &data, sizeof(data));
+}
+
+void cras_observer_notify_capture_mute(int muted, int mute_locked)
+{
+ struct cras_observer_alert_data_mute data;
+
+ data.muted = muted;
+ data.user_muted = 0;
+ data.mute_locked = mute_locked;
+ cras_alert_pending_data(g_observer->alerts.capture_mute,
+ &data, sizeof(data));
+}
+
+void cras_observer_notify_nodes(void)
+{
+ cras_alert_pending(g_observer->alerts.nodes);
+}
+
+void cras_observer_notify_active_node(enum CRAS_STREAM_DIRECTION dir,
+ cras_node_id_t node_id)
+{
+ struct cras_observer_alert_data_active_node data;
+
+ data.direction = dir;
+ data.node_id = node_id;
+ cras_alert_pending_data(g_observer->alerts.active_node,
+ &data, sizeof(data));
+}
+
+void cras_observer_notify_output_node_volume(cras_node_id_t node_id,
+ int32_t volume)
+{
+ struct cras_observer_alert_data_node_volume data;
+
+ data.node_id = node_id;
+ data.volume = volume;
+ cras_alert_pending_data(g_observer->alerts.output_node_volume,
+ &data, sizeof(data));
+}
+
+void cras_observer_notify_node_left_right_swapped(cras_node_id_t node_id,
+ int swapped)
+{
+ struct cras_observer_alert_data_node_lr_swapped data;
+
+ data.node_id = node_id;
+ data.swapped = swapped;
+ cras_alert_pending_data(g_observer->alerts.node_left_right_swapped,
+ &data, sizeof(data));
+}
+
+void cras_observer_notify_input_node_gain(cras_node_id_t node_id,
+ int32_t gain)
+{
+ struct cras_observer_alert_data_node_volume data;
+
+ data.node_id = node_id;
+ data.volume = gain;
+ cras_alert_pending_data(g_observer->alerts.input_node_gain,
+ &data, sizeof(data));
+}
+
+void cras_observer_notify_suspend_changed(int suspended)
+{
+ struct cras_observer_alert_data_suspend data;
+
+ data.suspended = suspended;
+ cras_alert_pending_data(g_observer->alerts.suspend_changed,
+ &data, sizeof(data));
+}
+
+void cras_observer_notify_num_active_streams(enum CRAS_STREAM_DIRECTION dir,
+ uint32_t num_active_streams)
+{
+ struct cras_observer_alert_data_streams data;
+ struct cras_alert *alert;
+
+ data.direction = dir;
+ data.num_active_streams = num_active_streams;
+ alert = g_observer->alerts.num_active_streams[dir];
+ if (!alert)
+ return;
+
+ cras_alert_pending_data(alert, &data, sizeof(data));
+}
diff --git a/cras/src/server/cras_observer.h b/cras/src/server/cras_observer.h
new file mode 100644
index 00000000..bbbaf896
--- /dev/null
+++ b/cras/src/server/cras_observer.h
@@ -0,0 +1,97 @@
+/* Copyright 2016 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_OBSERVER_H
+#define CRAS_OBSERVER_H
+
+#include "cras_observer_ops.h"
+
+struct cras_observer_client;
+
+/* Add an observer.
+ * Args:
+ * ops - Set callback function pointers in the operations that should be
+ * called for state changes, or NULL otherwise.
+ * context - Context pointer passed to the callbacks.
+ * Returns:
+ * Valid pointer to the client reference, or NULL on memory allocation
+ * error.
+ */
+struct cras_observer_client *cras_observer_add(
+ const struct cras_observer_ops *ops,
+ void *context);
+
+/* Retrieve the observed state changes.
+ * Args:
+ * client - The client to query.
+ * ops - Filled with the current values in the callback table.
+ */
+void cras_observer_get_ops(const struct cras_observer_client *client,
+ struct cras_observer_ops *ops);
+
+/* Update the observed state changes.
+ * Args:
+ * client - The client to modify.
+ * ops - Set callback function pointers in the operations that should be
+ * called for state changes, or NULL otherwise.
+ */
+void cras_observer_set_ops(struct cras_observer_client *client,
+ const struct cras_observer_ops *ops);
+
+/* Returns non-zero if the given ops are empty. */
+int cras_observer_ops_are_empty(const struct cras_observer_ops *ops);
+
+/* Remove this observer client.
+ * Args:
+ * client - The client to remove.
+ */
+void cras_observer_remove(struct cras_observer_client *client);
+
+/* Initialize the observer server. */
+int cras_observer_server_init();
+
+/* Destroy the observer server. */
+void cras_observer_server_free();
+
+/* Notify observers of output volume change. */
+void cras_observer_notify_output_volume(int32_t volume);
+
+/* Notify observers of output mute change. */
+void cras_observer_notify_output_mute(int muted, int user_muted,
+ int mute_locked);
+
+/* Notify observers of capture gain change. */
+void cras_observer_notify_capture_gain(int32_t gain);
+
+/* Notify observers of capture mute change. */
+void cras_observer_notify_capture_mute(int muted, int mute_locked);
+
+/* Notify observers of a nodes list change. */
+void cras_observer_notify_nodes(void);
+
+/* Notify observers of active output node change. */
+void cras_observer_notify_active_node(enum CRAS_STREAM_DIRECTION dir,
+ cras_node_id_t node_id);
+
+/* Notify observers of output node volume change. */
+void cras_observer_notify_output_node_volume(cras_node_id_t node_id,
+ int32_t volume);
+
+/* Notify observers of node left-right swap change. */
+void cras_observer_notify_node_left_right_swapped(cras_node_id_t node_id,
+ int swapped);
+
+/* Notify observers of input node gain change. */
+void cras_observer_notify_input_node_gain(cras_node_id_t node_id,
+ int32_t gain);
+
+/* Notify observers of suspend state changed. */
+void cras_observer_notify_suspend_changed(int suspended);
+
+/* Notify observers of the number of active streams. */
+void cras_observer_notify_num_active_streams(enum CRAS_STREAM_DIRECTION dir,
+ uint32_t num_active_streams);
+
+#endif /* CRAS_OBSERVER_H */
diff --git a/cras/src/server/cras_ramp.c b/cras/src/server/cras_ramp.c
new file mode 100644
index 00000000..e83dc511
--- /dev/null
+++ b/cras/src/server/cras_ramp.c
@@ -0,0 +1,150 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <syslog.h>
+
+#include "cras_ramp.h"
+
+
+/*
+ * State of cras_ramp:
+ * CRAS_RAMP_STATE_IDLE: No ramping is started, or a ramping is already done.
+ * CRAS_RAMP_STATE_UP: Ramping up from 0 to 1.
+ * CRAS_RAMP_STATE_DOWN: Ramping down from certain scaler to 0.
+ */
+enum CRAS_RAMP_STATE {
+ CRAS_RAMP_STATE_IDLE,
+ CRAS_RAMP_STATE_UP,
+ CRAS_RAMP_STATE_DOWN,
+};
+
+/*
+ * Struct to hold ramping information.
+ * Members:
+ * state: Current state. One of CRAS_RAMP_STATE.
+ * ramped_frames: Number of frames that have passed after starting ramping.
+ * duration_frames: The targeted number of frames for whole ramping duration.
+ * increment: The scaler increment that should be added to scaler for
+ * every frame.
+ * start_scaler: The initial scaler.
+ * cb: Callback function to call after ramping is done.
+ * cb_data: Data passed to cb.
+ */
+struct cras_ramp {
+ enum CRAS_RAMP_STATE state;
+ int ramped_frames;
+ int duration_frames;
+ float increment;
+ float start_scaler;
+ void (*cb)(void *data);
+ void *cb_data;
+};
+
+void cras_ramp_destroy(struct cras_ramp* ramp)
+{
+ free(ramp);
+}
+
+struct cras_ramp* cras_ramp_create()
+{
+ struct cras_ramp* ramp;
+ ramp = (struct cras_ramp*)malloc(sizeof(*ramp));
+ if (ramp == NULL) {
+ return NULL;
+ }
+ cras_ramp_reset(ramp);
+ return ramp;
+}
+
+int cras_ramp_reset(struct cras_ramp *ramp) {
+ ramp->state = CRAS_RAMP_STATE_IDLE;
+ ramp->ramped_frames = 0;
+ ramp->duration_frames = 0;
+ ramp->increment = 0;
+ ramp->start_scaler = 1.0;
+ return 0;
+}
+
+int cras_ramp_start(struct cras_ramp *ramp, int is_up, int duration_frames,
+ cras_ramp_cb cb, void *cb_data)
+{
+ struct cras_ramp_action action;
+
+ /* Get current scaler position so it can serve as new start scaler. */
+ action = cras_ramp_get_current_action(ramp);
+ if (action.type == CRAS_RAMP_ACTION_INVALID)
+ return -EINVAL;
+
+ /* Set initial scaler to current scaler so ramping up/down can be
+ * smoothly switched. */
+ if (is_up) {
+ ramp->state = CRAS_RAMP_STATE_UP;
+ if (action.type == CRAS_RAMP_ACTION_NONE)
+ ramp->start_scaler = 0;
+ else
+ ramp->start_scaler = action.scaler;
+ ramp->increment = (1 - ramp->start_scaler) / duration_frames;
+ } else {
+ ramp->state = CRAS_RAMP_STATE_DOWN;
+ if (action.type == CRAS_RAMP_ACTION_NONE)
+ ramp->start_scaler = 1;
+ else
+ ramp->start_scaler = action.scaler;
+
+ ramp->increment = -ramp->start_scaler / duration_frames;
+ }
+ ramp->ramped_frames = 0;
+ ramp->duration_frames = duration_frames;
+ ramp->cb = cb;
+ ramp->cb_data = cb_data;
+ return 0;
+}
+
+struct cras_ramp_action cras_ramp_get_current_action(const struct cras_ramp *ramp)
+{
+ struct cras_ramp_action action;
+
+ if (ramp->ramped_frames < 0) {
+ action.type = CRAS_RAMP_ACTION_INVALID;
+ action.scaler = 1.0;
+ action.increment = 0.0;
+ return action;
+ }
+
+ switch (ramp->state) {
+ case CRAS_RAMP_STATE_IDLE:
+ action.type = CRAS_RAMP_ACTION_NONE;
+ action.scaler = 1.0;
+ action.increment = 0.0;
+ break;
+ case CRAS_RAMP_STATE_DOWN:
+ action.type = CRAS_RAMP_ACTION_PARTIAL;
+ action.scaler = ramp->start_scaler +
+ ramp->ramped_frames * ramp->increment;
+ action.increment = ramp->increment;
+ break;
+ case CRAS_RAMP_STATE_UP:
+ action.type = CRAS_RAMP_ACTION_PARTIAL;
+ action.scaler = ramp->start_scaler +
+ ramp->ramped_frames * ramp->increment;
+ action.increment = ramp->increment;
+ break;
+ }
+ return action;
+}
+
+int cras_ramp_update_ramped_frames(
+ struct cras_ramp *ramp, int num_frames)
+{
+ if (ramp->state == CRAS_RAMP_STATE_IDLE)
+ return -EINVAL;
+ ramp->ramped_frames += num_frames;
+ if (ramp->ramped_frames >= ramp->duration_frames) {
+ ramp->state = CRAS_RAMP_STATE_IDLE;
+ if (ramp->cb)
+ ramp->cb(ramp->cb_data);
+ }
+ return 0;
+}
diff --git a/cras/src/server/cras_ramp.h b/cras/src/server/cras_ramp.h
new file mode 100644
index 00000000..5b1acfb0
--- /dev/null
+++ b/cras/src/server/cras_ramp.h
@@ -0,0 +1,74 @@
+/* Copyright 2016 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_RAMP_H_
+#define CRAS_RAMP_H_
+
+#include "cras_iodev.h"
+
+struct cras_ramp;
+
+/*
+ * Infomation telling user how to do ramping.
+ * action CRAS_RAMP_ACTION_NONE: No scale should be applied.
+ * action CRAS_RAMP_ACTION_PARTIAL: scale sample by sample starting from scaler
+ * and increase increment for each sample.
+ * action CRAS_RAMP_ACTION_INVALID: There is an error in cras_ramp.
+ */
+enum CRAS_RAMP_ACTION_TYPE {
+ CRAS_RAMP_ACTION_NONE,
+ CRAS_RAMP_ACTION_PARTIAL,
+ CRAS_RAMP_ACTION_INVALID,
+};
+
+/*
+ * Struct to hold current ramping action for user.
+ * Members:
+ * type: See CRAS_RAMP_ACTION_TYPE.
+ * scaler: The initial scaler to be applied.
+ * increment: The scaler increment that should be added to scaler for every
+ * frame.
+ */
+struct cras_ramp_action {
+ enum CRAS_RAMP_ACTION_TYPE type;
+ float scaler;
+ float increment;
+};
+
+typedef void (*cras_ramp_cb)(void *arg);
+
+/* Creates a ramp. */
+struct cras_ramp* cras_ramp_create();
+
+/* Destroys a ramp. */
+void cras_ramp_destroy(struct cras_ramp* ramp);
+
+/* Starts ramping up from 0 to 1 or from 1 to 0 for duration_frames frames.
+ * Args:
+ * ramp[in]: The ramp struct to start.
+ * is_up[in]: 1 to ramp up and 0 to ramp down.
+ * duration_frames[in]: Ramp duration in frames.
+ * cb[in]: The callback function to call after ramping is done. User can set
+ * cb to turn off speaker/headphone switch after ramping down
+ * is done.
+ * cb_data[in]: The data passed to callback function.
+ * Returns:
+ * 0 on success; negative error code on failure.
+ */
+int cras_ramp_start(struct cras_ramp *ramp, int is_up, int duration_frames,
+ cras_ramp_cb cb, void *cb_data);
+
+/* Resets ramp and cancels current ramping. */
+int cras_ramp_reset(struct cras_ramp *ramp);
+
+/* Gets current ramp action. */
+struct cras_ramp_action cras_ramp_get_current_action(
+ const struct cras_ramp *ramp);
+
+/* Updates number of samples that went through ramping. */
+int cras_ramp_update_ramped_frames(
+ struct cras_ramp *ramp, int num_frames);
+
+#endif /* CRAS_RAMP_H_ */
diff --git a/cras/src/server/cras_rclient.c b/cras/src/server/cras_rclient.c
index 957bdf35..6a68351b 100644
--- a/cras/src/server/cras_rclient.c
+++ b/cras/src/server/cras_rclient.c
@@ -13,6 +13,7 @@
#include "cras_iodev.h"
#include "cras_iodev_list.h"
#include "cras_messages.h"
+#include "cras_observer.h"
#include "cras_rclient.h"
#include "cras_rstream.h"
#include "cras_system_state.h"
@@ -26,6 +27,7 @@
* fd - Connection for client communication.
*/
struct cras_rclient {
+ struct cras_observer_client *observer;
size_t id;
int fd;
};
@@ -40,6 +42,7 @@ static int handle_client_stream_connect(struct cras_rclient *client,
struct cras_audio_format remote_fmt;
struct cras_rstream_config stream_config;
int rc;
+ int stream_fds[2];
unpack_cras_audio_format(&remote_fmt, &msg->format);
@@ -77,10 +80,10 @@ static int handle_client_stream_connect(struct cras_rclient *client,
0, /* No error. */
msg->stream_id,
&remote_fmt,
- cras_rstream_input_shm_key(stream),
- cras_rstream_output_shm_key(stream),
cras_rstream_get_total_shm_size(stream));
- rc = cras_rclient_send_message(client, &reply.header);
+ stream_fds[0] = cras_rstream_input_shm_fd(stream);
+ stream_fds[1] = cras_rstream_output_shm_fd(stream);
+ rc = cras_rclient_send_message(client, &reply.header, stream_fds, 2);
if (rc < 0) {
syslog(LOG_ERR, "Failed to send connected messaged\n");
stream_list_rm(cras_iodev_list_get_stream_list(),
@@ -93,8 +96,8 @@ static int handle_client_stream_connect(struct cras_rclient *client,
reply_err:
/* Send the error code to the client. */
cras_fill_client_stream_connected(&reply, rc, msg->stream_id,
- &remote_fmt, 0, 0, 0);
- cras_rclient_send_message(client, &reply.header);
+ &remote_fmt, 0);
+ cras_rclient_send_message(client, &reply.header, NULL, 0);
if (aud_fd >= 0)
close(aud_fd);
@@ -122,7 +125,211 @@ static void dump_audio_thread_info(struct cras_rclient *client)
state = cras_system_state_get_no_lock();
audio_thread_dump_thread_info(cras_iodev_list_get_audio_thread(),
&state->audio_debug_info);
- cras_rclient_send_message(client, &msg.header);
+ cras_rclient_send_message(client, &msg.header, NULL, 0);
+}
+
+static void handle_get_hotword_models(struct cras_rclient *client,
+ cras_node_id_t node_id)
+{
+ struct cras_client_get_hotword_models_ready *msg;
+ char *hotword_models;
+ unsigned hotword_models_size;
+ uint8_t buf[CRAS_CLIENT_MAX_MSG_SIZE];
+
+ msg = (struct cras_client_get_hotword_models_ready *)buf;
+ hotword_models = cras_iodev_list_get_hotword_models(node_id);
+ if (!hotword_models)
+ goto empty_reply;
+ hotword_models_size = strlen(hotword_models);
+ if (hotword_models_size + sizeof(*msg) > CRAS_CLIENT_MAX_MSG_SIZE) {
+ free(hotword_models);
+ goto empty_reply;
+ }
+
+ cras_fill_client_get_hotword_models_ready(msg, hotword_models,
+ hotword_models_size);
+ cras_rclient_send_message(client, &msg->header, NULL, 0);
+ free(hotword_models);
+ return;
+
+empty_reply:
+ cras_fill_client_get_hotword_models_ready(msg, NULL, 0);
+ cras_rclient_send_message(client, &msg->header, NULL, 0);
+}
+
+/* Client notification callback functions. */
+
+static void send_output_volume_changed(void *context, int32_t volume)
+{
+ struct cras_client_volume_changed msg;
+ struct cras_rclient *client = (struct cras_rclient *)context;
+
+ cras_fill_client_output_volume_changed(&msg, volume);
+ cras_rclient_send_message(client, &msg.header, NULL, 0);
+}
+
+static void send_output_mute_changed(void *context, int muted,
+ int user_muted, int mute_locked)
+{
+ struct cras_client_mute_changed msg;
+ struct cras_rclient *client = (struct cras_rclient *)context;
+
+ cras_fill_client_output_mute_changed(&msg, muted,
+ user_muted, mute_locked);
+ cras_rclient_send_message(client, &msg.header, NULL, 0);
+}
+
+static void send_capture_gain_changed(void *context, int32_t gain)
+{
+ struct cras_client_volume_changed msg;
+ struct cras_rclient *client = (struct cras_rclient *)context;
+
+ cras_fill_client_capture_gain_changed(&msg, gain);
+ cras_rclient_send_message(client, &msg.header, NULL, 0);
+}
+
+static void send_capture_mute_changed(void *context, int muted, int mute_locked)
+{
+ struct cras_client_mute_changed msg;
+ struct cras_rclient *client = (struct cras_rclient *)context;
+
+ cras_fill_client_capture_mute_changed(&msg, muted, mute_locked);
+ cras_rclient_send_message(client, &msg.header, NULL, 0);
+}
+
+static void send_nodes_changed(void *context)
+{
+ struct cras_client_nodes_changed msg;
+ struct cras_rclient *client = (struct cras_rclient *)context;
+
+ cras_fill_client_nodes_changed(&msg);
+ cras_rclient_send_message(client, &msg.header, NULL, 0);
+}
+
+static void send_active_node_changed(void *context,
+ enum CRAS_STREAM_DIRECTION dir,
+ cras_node_id_t node_id)
+{
+ struct cras_client_active_node_changed msg;
+ struct cras_rclient *client = (struct cras_rclient *)context;
+
+ cras_fill_client_active_node_changed(&msg, dir, node_id);
+ cras_rclient_send_message(client, &msg.header, NULL, 0);
+}
+
+static void send_output_node_volume_changed(void *context,
+ cras_node_id_t node_id,
+ int32_t volume)
+{
+ struct cras_client_node_value_changed msg;
+ struct cras_rclient *client = (struct cras_rclient *)context;
+
+ cras_fill_client_output_node_volume_changed(&msg, node_id, volume);
+ cras_rclient_send_message(client, &msg.header, NULL, 0);
+}
+
+static void send_node_left_right_swapped_changed(void *context,
+ cras_node_id_t node_id,
+ int swapped)
+{
+ struct cras_client_node_value_changed msg;
+ struct cras_rclient *client = (struct cras_rclient *)context;
+
+ cras_fill_client_node_left_right_swapped_changed(
+ &msg, node_id, swapped);
+ cras_rclient_send_message(client, &msg.header, NULL, 0);
+}
+
+static void send_input_node_gain_changed(void *context,
+ cras_node_id_t node_id,
+ int32_t gain)
+{
+ struct cras_client_node_value_changed msg;
+ struct cras_rclient *client = (struct cras_rclient *)context;
+
+ cras_fill_client_input_node_gain_changed(&msg, node_id, gain);
+ cras_rclient_send_message(client, &msg.header, NULL, 0);
+}
+
+static void send_num_active_streams_changed(void *context,
+ enum CRAS_STREAM_DIRECTION dir,
+ uint32_t num_active_streams)
+{
+ struct cras_client_num_active_streams_changed msg;
+ struct cras_rclient *client = (struct cras_rclient *)context;
+
+ cras_fill_client_num_active_streams_changed(
+ &msg, dir, num_active_streams);
+ cras_rclient_send_message(client, &msg.header, NULL, 0);
+}
+
+static void register_for_notification(struct cras_rclient *client,
+ enum CRAS_CLIENT_MESSAGE_ID msg_id,
+ int do_register)
+{
+ struct cras_observer_ops observer_ops;
+ int empty;
+
+ cras_observer_get_ops(client->observer, &observer_ops);
+
+ switch (msg_id) {
+ case CRAS_CLIENT_OUTPUT_VOLUME_CHANGED:
+ observer_ops.output_volume_changed =
+ do_register ? send_output_volume_changed : NULL;
+ break;
+ case CRAS_CLIENT_OUTPUT_MUTE_CHANGED:
+ observer_ops.output_mute_changed =
+ do_register ? send_output_mute_changed : NULL;
+ break;
+ case CRAS_CLIENT_CAPTURE_GAIN_CHANGED:
+ observer_ops.capture_gain_changed =
+ do_register ? send_capture_gain_changed : NULL;
+ break;
+ case CRAS_CLIENT_CAPTURE_MUTE_CHANGED:
+ observer_ops.capture_mute_changed =
+ do_register ? send_capture_mute_changed : NULL;
+ break;
+ case CRAS_CLIENT_NODES_CHANGED:
+ observer_ops.nodes_changed =
+ do_register ? send_nodes_changed : NULL;
+ break;
+ case CRAS_CLIENT_ACTIVE_NODE_CHANGED:
+ observer_ops.active_node_changed =
+ do_register ? send_active_node_changed : NULL;
+ break;
+ case CRAS_CLIENT_OUTPUT_NODE_VOLUME_CHANGED:
+ observer_ops.output_node_volume_changed =
+ do_register ? send_output_node_volume_changed : NULL;
+ break;
+ case CRAS_CLIENT_NODE_LEFT_RIGHT_SWAPPED_CHANGED:
+ observer_ops.node_left_right_swapped_changed =
+ do_register ? send_node_left_right_swapped_changed : NULL;
+ break;
+ case CRAS_CLIENT_INPUT_NODE_GAIN_CHANGED:
+ observer_ops.input_node_gain_changed =
+ do_register ? send_input_node_gain_changed : NULL;
+ break;
+ case CRAS_CLIENT_NUM_ACTIVE_STREAMS_CHANGED:
+ observer_ops.num_active_streams_changed =
+ do_register ? send_num_active_streams_changed : NULL;
+ break;
+ default:
+ syslog(LOG_ERR,
+ "Invalid client notification message ID: %u", msg_id);
+ break;
+ }
+
+ empty = cras_observer_ops_are_empty(&observer_ops);
+ if (client->observer) {
+ if (empty) {
+ cras_observer_remove(client->observer);
+ client->observer = NULL;
+ } else {
+ cras_observer_set_ops(client->observer, &observer_ops);
+ }
+ } else if (!empty) {
+ client->observer = cras_observer_add(&observer_ops, client);
+ }
}
/*
@@ -135,16 +342,18 @@ struct cras_rclient *cras_rclient_create(int fd, size_t id)
{
struct cras_rclient *client;
struct cras_client_connected msg;
+ int state_fd;
- client = calloc(1, sizeof(struct cras_rclient));
+ client = (struct cras_rclient *)calloc(1, sizeof(struct cras_rclient));
if (!client)
return NULL;
client->fd = fd;
client->id = id;
- cras_fill_client_connected(&msg, client->id, cras_sys_state_shm_key());
- cras_rclient_send_message(client, &msg.header);
+ cras_fill_client_connected(&msg, client->id);
+ state_fd = cras_sys_state_shm_fd();
+ cras_rclient_send_message(client, &msg.header, &state_fd, 1);
return client;
}
@@ -152,6 +361,7 @@ struct cras_rclient *cras_rclient_create(int fd, size_t id)
/* Removes all streams that the client owns and destroys it. */
void cras_rclient_destroy(struct cras_rclient *client)
{
+ cras_observer_remove(client->observer);
stream_list_rm_all_client_streams(
cras_iodev_list_get_stream_list(), client);
free(client);
@@ -260,8 +470,9 @@ int cras_rclient_message_from_client(struct cras_rclient *client,
case CRAS_SERVER_TEST_DEV_COMMAND: {
const struct cras_test_dev_command *m =
(const struct cras_test_dev_command *)msg;
- cras_iodev_list_test_dev_command(m->iodev_idx, m->command,
- m->data_len, m->data);
+ cras_iodev_list_test_dev_command(
+ m->iodev_idx, (enum CRAS_TEST_IODEV_CMD)m->command,
+ m->data_len, m->data);
break;
}
case CRAS_SERVER_SUSPEND:
@@ -270,6 +481,35 @@ int cras_rclient_message_from_client(struct cras_rclient *client,
case CRAS_SERVER_RESUME:
cras_system_set_suspended(0);
break;
+ case CRAS_CONFIG_GLOBAL_REMIX: {
+ const struct cras_config_global_remix *m =
+ (const struct cras_config_global_remix *)msg;
+ audio_thread_config_global_remix(
+ cras_iodev_list_get_audio_thread(),
+ m->num_channels,
+ m->coefficient);
+ break;
+ }
+ case CRAS_SERVER_GET_HOTWORD_MODELS: {
+ handle_get_hotword_models(client,
+ ((const struct cras_get_hotword_models *)msg)->node_id);
+ break;
+ }
+ case CRAS_SERVER_SET_HOTWORD_MODEL: {
+ const struct cras_set_hotword_model *m =
+ (const struct cras_set_hotword_model *)msg;
+ cras_iodev_list_set_hotword_model(m->node_id,
+ m->model_name);
+ break;
+ }
+ case CRAS_SERVER_REGISTER_NOTIFICATION: {
+ const struct cras_register_notification *m =
+ (struct cras_register_notification *)msg;
+ register_for_notification(
+ client, (enum CRAS_CLIENT_MESSAGE_ID)m->msg_id,
+ m->do_register);
+ break;
+ }
default:
break;
}
@@ -279,8 +519,11 @@ int cras_rclient_message_from_client(struct cras_rclient *client,
/* Sends a message to the client. */
int cras_rclient_send_message(const struct cras_rclient *client,
- const struct cras_client_message *msg)
+ const struct cras_client_message *msg,
+ int *fds,
+ unsigned int num_fds)
{
- return write(client->fd, msg, msg->length);
+ return cras_send_with_fds(client->fd, (const void *)msg, msg->length,
+ fds, num_fds);
}
diff --git a/cras/src/server/cras_rclient.h b/cras/src/server/cras_rclient.h
index 57cf04a5..26e4f3b9 100644
--- a/cras/src/server/cras_rclient.h
+++ b/cras/src/server/cras_rclient.h
@@ -44,10 +44,14 @@ int cras_rclient_message_from_client(struct cras_rclient *client,
* Args:
* client - The client to send the message to.
* msg - The message to send.
+ * fds - Array of file descriptors or null
+ * num_fds - Number of entries in the fds array.
* Returns:
* number of bytes written on success, otherwise a negative error code.
*/
int cras_rclient_send_message(const struct cras_rclient *client,
- const struct cras_client_message *msg);
+ const struct cras_client_message *msg,
+ int *fds,
+ unsigned int num_fds);
#endif /* CRAS_RCLIENT_H_ */
diff --git a/cras/src/server/cras_rstream.c b/cras/src/server/cras_rstream.c
index 5296b5da..8b9b0468 100644
--- a/cras/src/server/cras_rstream.c
+++ b/cras/src/server/cras_rstream.c
@@ -2,8 +2,11 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
+
+#include <fcntl.h>
#include <stdint.h>
-#include <sys/shm.h>
+#include <sys/mman.h>
+#include <sys/types.h>
#include <syslog.h>
#include "cras_audio_area.h"
@@ -21,8 +24,7 @@ static int setup_shm(struct cras_rstream *stream,
struct cras_audio_shm *shm,
struct rstream_shm_info *shm_info)
{
- size_t used_size, samples_size, total_size, frame_bytes;
- int loops = 0;
+ size_t used_size, samples_size, frame_bytes;
const struct cras_audio_format *fmt = &stream->format;
if (shm->area != NULL) /* already setup */
@@ -32,25 +34,24 @@ static int setup_shm(struct cras_rstream *stream,
fmt->num_channels;
used_size = stream->buffer_frames * frame_bytes;
samples_size = used_size * CRAS_NUM_SHM_BUFFERS;
- total_size = sizeof(struct cras_audio_shm_area) + samples_size;
-
- /* Find an available shm key. */
- do {
- shm_info->shm_key = getpid() + stream->stream_id + loops;
- shm_info->shm_id = shmget(shm_info->shm_key,
- total_size,
- IPC_CREAT | IPC_EXCL | 0660);
- } while (shm_info->shm_id < 0 && loops++ < 100);
- if (shm_info->shm_id < 0) {
- syslog(LOG_ERR, "shmget");
- return shm_info->shm_id;
+ shm_info->length = sizeof(struct cras_audio_shm_area) + samples_size;
+
+ snprintf(shm_info->shm_name, sizeof(shm_info->shm_name),
+ "/cras-%d-stream-%08x", getpid(), stream->stream_id);
+
+ shm_info->shm_fd = cras_shm_open_rw(shm_info->shm_name, shm_info->length);
+ if (shm_info->shm_fd < 0)
+ return shm_info->shm_fd;
+
+ /* mmap shm. */
+ shm->area = mmap(NULL, shm_info->length,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ shm_info->shm_fd, 0);
+ if (shm->area == (struct cras_audio_shm_area *)-1) {
+ close(shm_info->shm_fd);
+ return errno;
}
- /* Attach to shm and clear it. */
- shm->area = shmat(shm_info->shm_id, NULL, 0);
- if (shm->area == (void *)-1)
- return -ENOMEM;
- memset(shm->area, 0, total_size);
cras_shm_set_volume_scaler(shm, 1.0);
/* Set up config and copy to shared area. */
cras_shm_set_frame_bytes(shm, frame_bytes);
@@ -84,6 +85,7 @@ static inline int buffer_meets_size_limit(size_t buffer_size, size_t rate)
/* Verifies that the given stream parameters are valid. */
static int verify_rstream_parameters(enum CRAS_STREAM_DIRECTION direction,
const struct cras_audio_format *format,
+ enum CRAS_STREAM_TYPE stream_type,
size_t buffer_frames,
size_t cb_threshold,
struct cras_rclient *client,
@@ -114,6 +116,11 @@ static int verify_rstream_parameters(enum CRAS_STREAM_DIRECTION direction,
syslog(LOG_ERR, "rstream: Invalid direction.\n");
return -EINVAL;
}
+ if (stream_type < CRAS_STREAM_TYPE_DEFAULT ||
+ stream_type >= CRAS_STREAM_NUM_TYPES) {
+ syslog(LOG_ERR, "rstream: Invalid stream type.\n");
+ return -EINVAL;
+ }
if (!buffer_meets_size_limit(cb_threshold, format->frame_rate)) {
syslog(LOG_ERR, "rstream: cb_threshold too low\n");
return -EINVAL;
@@ -130,6 +137,7 @@ int cras_rstream_create(struct cras_rstream_config *config,
int rc;
rc = verify_rstream_parameters(config->direction, config->format,
+ config->stream_type,
config->buffer_frames,
config->cb_threshold, config->client,
stream_out);
@@ -178,9 +186,9 @@ void cras_rstream_destroy(struct cras_rstream *stream)
cras_system_state_stream_removed(stream->direction);
close(stream->fd);
if (stream->shm.area != NULL) {
- shmdt(stream->shm.area);
- shmctl(stream->shm_info.shm_id, IPC_RMID,
- (void *)stream->shm.area);
+ munmap(stream->shm.area, stream->shm_info.length);
+ cras_shm_close_unlink(stream->shm_info.shm_name,
+ stream->shm_info.shm_fd);
cras_audio_area_destroy(stream->audio_area);
}
buffer_share_destroy(stream->buf_state);
@@ -197,10 +205,19 @@ void cras_rstream_record_fetch_interval(struct cras_rstream *rstream,
if (timespec_after(&ts, &rstream->longest_fetch_interval))
rstream->longest_fetch_interval = ts;
}
- rstream->last_fetch_ts = *now;
}
-int cras_rstream_request_audio(const struct cras_rstream *stream)
+static void init_audio_message(struct audio_message *msg,
+ enum CRAS_AUDIO_MESSAGE_ID id,
+ uint32_t frames)
+{
+ memset(msg, 0, sizeof(*msg));
+ msg->id = id;
+ msg->frames = frames;
+}
+
+int cras_rstream_request_audio(struct cras_rstream *stream,
+ const struct timespec *now)
{
struct audio_message msg;
int rc;
@@ -209,9 +226,13 @@ int cras_rstream_request_audio(const struct cras_rstream *stream)
if (stream->direction != CRAS_STREAM_OUTPUT)
return 0;
- msg.id = AUDIO_MESSAGE_REQUEST_DATA;
- msg.frames = stream->cb_threshold;
+ stream->last_fetch_ts = *now;
+
+ init_audio_message(&msg, AUDIO_MESSAGE_REQUEST_DATA,
+ stream->cb_threshold);
rc = write(stream->fd, &msg, sizeof(msg));
+ if (rc < 0)
+ return -errno;
return rc;
}
@@ -222,9 +243,10 @@ int cras_rstream_audio_ready(struct cras_rstream *stream, size_t count)
cras_shm_buffer_write_complete(&stream->shm);
- msg.id = AUDIO_MESSAGE_DATA_READY;
- msg.frames = count;
+ init_audio_message(&msg, AUDIO_MESSAGE_DATA_READY, count);
rc = write(stream->fd, &msg, sizeof(msg));
+ if (rc < 0)
+ return -errno;
return rc;
}
@@ -234,8 +256,10 @@ int cras_rstream_get_audio_request_reply(const struct cras_rstream *stream)
int rc;
rc = read(stream->fd, &msg, sizeof(msg));
- if (rc < 0 || msg.error < 0)
- return -EIO;
+ if (rc < 0)
+ return -errno;
+ if (msg.error < 0)
+ return msg.error;
return 0;
}
@@ -312,7 +336,8 @@ unsigned int cras_rstream_dev_offset(const struct cras_rstream *rstream,
void cras_rstream_update_queued_frames(struct cras_rstream *rstream)
{
const struct cras_audio_shm *shm = cras_rstream_output_shm(rstream);
- rstream->queued_frames = cras_shm_get_frames(shm);
+ rstream->queued_frames = MIN(cras_shm_get_frames(shm),
+ rstream->buffer_frames);
}
unsigned int cras_rstream_playable_frames(struct cras_rstream *rstream,
diff --git a/cras/src/server/cras_rstream.h b/cras/src/server/cras_rstream.h
index 36709a7b..1a227176 100644
--- a/cras/src/server/cras_rstream.h
+++ b/cras/src/server/cras_rstream.h
@@ -16,12 +16,14 @@ struct cras_rclient;
struct dev_mix;
/* Holds identifiers for an shm segment.
- * shm_key - Key shared with client to access shm.
- * shm_id - Returned from shmget.
+ * shm_fd - File descriptor shared with client to access shm.
+ * shm_name - Name of the shm area.
+ * length - Size of the shm region.
*/
struct rstream_shm_info {
- int shm_key;
- int shm_id;
+ int shm_fd;
+ char shm_name[NAME_MAX];
+ size_t length;
};
/* Holds informations about the master active device.
@@ -200,15 +202,15 @@ static inline void cras_rstream_set_is_draining(struct cras_rstream *stream,
}
/* Gets the shm key used to find the outputshm region. */
-static inline int cras_rstream_output_shm_key(const struct cras_rstream *stream)
+static inline int cras_rstream_output_shm_fd(const struct cras_rstream *stream)
{
- return stream->shm_info.shm_key;
+ return stream->shm_info.shm_fd;
}
/* Gets the shm key used to find the input shm region. */
-static inline int cras_rstream_input_shm_key(const struct cras_rstream *stream)
+static inline int cras_rstream_input_shm_fd(const struct cras_rstream *stream)
{
- return stream->shm_info.shm_key;
+ return stream->shm_info.shm_fd;
}
/* Gets the total size of shm memory allocated. */
@@ -254,7 +256,8 @@ void cras_rstream_record_fetch_interval(struct cras_rstream *rstream,
const struct timespec *now);
/* Requests min_req frames from the client. */
-int cras_rstream_request_audio(const struct cras_rstream *stream);
+int cras_rstream_request_audio(struct cras_rstream *stream,
+ const struct timespec *now);
/* Tells a capture client that count frames are ready. */
int cras_rstream_audio_ready(struct cras_rstream *stream, size_t count);
diff --git a/cras/src/server/cras_server.c b/cras/src/server/cras_server.c
index 18aef8a5..ff5e601c 100644
--- a/cras/src/server/cras_server.c
+++ b/cras/src/server/cras_server.c
@@ -5,7 +5,9 @@
#define _GNU_SOURCE /* Needed for Linux socket credential passing. */
+#ifdef CRAS_DBUS
#include <dbus/dbus.h>
+#endif
#include <errno.h>
#include <poll.h>
#include <stdint.h>
@@ -21,25 +23,32 @@
#include <syslog.h>
#include <unistd.h>
+#ifdef CRAS_DBUS
+#include "cras_a2dp_endpoint.h"
#include "cras_bt_manager.h"
#include "cras_bt_device.h"
-#include "cras_a2dp_endpoint.h"
-#include "cras_config.h"
+#include "cras_bt_player.h"
#include "cras_dbus.h"
#include "cras_dbus_control.h"
#include "cras_hfp_ag_profile.h"
+#include "cras_telephony.h"
+#endif
+#include "cras_alert.h"
+#include "cras_config.h"
+#include "cras_device_monitor.h"
#include "cras_iodev_list.h"
#include "cras_main_message.h"
#include "cras_messages.h"
#include "cras_metrics.h"
+#include "cras_observer.h"
#include "cras_rclient.h"
#include "cras_server.h"
#include "cras_server_metrics.h"
#include "cras_system_state.h"
-#include "cras_telephony.h"
#include "cras_tm.h"
#include "cras_udev.h"
#include "cras_util.h"
+#include "cras_mix.h"
#include "utlist.h"
/* Store a list of clients that are attached to the server.
@@ -108,9 +117,10 @@ static void handle_message_from_client(struct attached_client *client)
struct cras_server_message *msg;
int nread;
int fd;
+ unsigned int num_fds = 1;
msg = (struct cras_server_message *)buf;
- nread = cras_recv_with_fd(client->fd, buf, sizeof(buf), &fd);
+ nread = cras_recv_with_fds(client->fd, buf, sizeof(buf), &fd, &num_fds);
if (nread < sizeof(msg->length))
goto read_error;
if (msg->length != nread)
@@ -121,7 +131,14 @@ static void handle_message_from_client(struct attached_client *client)
read_error:
if (fd != -1)
close(fd);
- syslog(LOG_DEBUG, "read err, removing client %zu", client->id);
+ switch (nread) {
+ case 0:
+ break;
+ default:
+ syslog(LOG_DEBUG, "read err [%d] '%s', removing client %zu",
+ -nread, strerror(-nread), client->id);
+ break;
+ }
remove_client(client);
}
@@ -301,6 +318,60 @@ void check_output_exists(struct cras_timer *t, void *data)
cras_metrics_log_event(kNoCodecsFoundMetric);
}
+#if defined(__amd64__)
+/* CPU detection - probaby best to move this elsewhere */
+static void cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
+ unsigned int *edx, unsigned int op)
+{
+ __asm__ __volatile__ (
+ "cpuid"
+ : "=a" (*eax),
+ "=b" (*ebx),
+ "=c" (*ecx),
+ "=d" (*edx)
+ : "a" (op), "c" (0)
+ );
+}
+
+static unsigned int cpu_x86_flags(void)
+{
+ unsigned int eax, ebx, ecx, edx, id;
+ unsigned int cpu_flags = 0;
+
+ cpuid(&id, &ebx, &ecx, &edx, 0);
+
+ if (id >= 1) {
+ cpuid(&eax, &ebx, &ecx, &edx, 1);
+
+ if (ecx & (1 << 20))
+ cpu_flags |= CPU_X86_SSE4_2;
+
+ if (ecx & (1 << 28))
+ cpu_flags |= CPU_X86_AVX;
+
+ if (ecx & (1 << 12))
+ cpu_flags |= CPU_X86_FMA;
+ }
+
+ if (id >= 7) {
+ cpuid(&eax, &ebx, &ecx, &edx, 7);
+
+ if (ebx & (1 << 5))
+ cpu_flags |= CPU_X86_AVX2;
+ }
+
+ return cpu_flags;
+}
+#endif
+
+int cpu_get_flags(void)
+{
+#if defined(__amd64__)
+ return cpu_x86_flags();
+#endif
+ return 0;
+}
+
/*
* Exported Interface.
*/
@@ -310,6 +381,12 @@ int cras_server_init()
/* Log to syslog. */
openlog("cras_server", LOG_PID, LOG_USER);
+ /* Initialize global observer. */
+ cras_observer_server_init();
+
+ /* init mixer with CPU capabilities */
+ cras_mix_init(cpu_get_flags());
+
/* Allow clients to register callbacks for file descriptors.
* add_select_fd and rm_select_fd will add and remove file descriptors
* from the list that are passed to select in the main loop below. */
@@ -320,11 +397,12 @@ int cras_server_init()
return 0;
}
-int cras_server_run()
+int cras_server_run(unsigned int profile_disable_mask)
{
static const unsigned int OUTPUT_CHECK_MS = 5 * 1000;
-
+#ifdef CRAS_DBUS
DBusConnection *dbus_conn;
+#endif
int socket_fd = -1;
int rc = 0;
const char *sockdir;
@@ -341,20 +419,30 @@ int cras_server_run()
pollfds = malloc(sizeof(*pollfds) * pollfds_size);
cras_udev_start_sound_subsystem_monitor();
+#ifdef CRAS_DBUS
cras_bt_device_start_monitor();
+#endif
cras_server_metrics_init();
+ cras_device_monitor_init();
+
+#ifdef CRAS_DBUS
dbus_threads_init_default();
dbus_conn = cras_dbus_connect_system_bus();
if (dbus_conn) {
cras_bt_start(dbus_conn);
- cras_hfp_ag_profile_create(dbus_conn);
- cras_hsp_ag_profile_create(dbus_conn);
+ if (!(profile_disable_mask & CRAS_SERVER_PROFILE_MASK_HFP))
+ cras_hfp_ag_profile_create(dbus_conn);
+ if (!(profile_disable_mask & CRAS_SERVER_PROFILE_MASK_HSP))
+ cras_hsp_ag_profile_create(dbus_conn);
cras_telephony_start(dbus_conn);
- cras_a2dp_endpoint_create(dbus_conn);
+ if (!(profile_disable_mask & CRAS_SERVER_PROFILE_MASK_A2DP))
+ cras_a2dp_endpoint_create(dbus_conn);
+ cras_bt_player_create(dbus_conn);
cras_dbus_control_start(dbus_conn);
}
+#endif
socket_fd = socket(PF_UNIX, SOCK_SEQPACKET, 0);
if (socket_fd < 0) {
@@ -464,8 +552,10 @@ int cras_server_run()
cleanup_select_fds(&server_instance);
+#ifdef CRAS_DBUS
if (dbus_conn)
cras_dbus_dispatch(dbus_conn);
+#endif
cras_alert_process_all_pending_alerts();
}
@@ -476,6 +566,7 @@ bail:
unlink(addr.sun_path);
}
free(pollfds);
+ cras_observer_server_free();
return rc;
}
@@ -484,5 +575,5 @@ void cras_server_send_to_all_clients(const struct cras_client_message *msg)
struct attached_client *client;
DL_FOREACH(server_instance.clients_head, client)
- cras_rclient_send_message(client->client, msg);
+ cras_rclient_send_message(client->client, msg, NULL, 0);
}
diff --git a/cras/src/server/cras_server.h b/cras/src/server/cras_server.h
index a52279b6..aff9b7a1 100644
--- a/cras/src/server/cras_server.h
+++ b/cras/src/server/cras_server.h
@@ -9,6 +9,13 @@
#ifndef CRAS_SERVER_H_
#define CRAS_SERVER_H_
+/*
+ * Bitmask for cras_server_run() argument profile_disable_mask
+ */
+#define CRAS_SERVER_PROFILE_MASK_HFP (1 << 0)
+#define CRAS_SERVER_PROFILE_MASK_HSP (1 << 1)
+#define CRAS_SERVER_PROFILE_MASK_A2DP (1 << 2)
+
struct cras_client_message;
/* Initialize some server setup. Mainly to add the select handler first
@@ -19,7 +26,7 @@ int cras_server_init();
/* Runs the CRAS server. Open the main socket and begin listening for
* connections and for messages from clients that have connected.
*/
-int cras_server_run();
+int cras_server_run(unsigned int profile_disable_mask);
/* Send a message to all attached clients. */
void cras_server_send_to_all_clients(const struct cras_client_message *msg);
diff --git a/cras/src/server/cras_server_metrics.c b/cras/src/server/cras_server_metrics.c
index 966d515d..c824407e 100644
--- a/cras/src/server/cras_server_metrics.c
+++ b/cras/src/server/cras_server_metrics.c
@@ -5,6 +5,7 @@
#include <errno.h>
#include <stdio.h>
+#include <string.h>
#include <syslog.h>
#include "cras_metrics.h"
@@ -24,16 +25,24 @@ struct cras_server_metrics_message {
unsigned data;
};
+static void init_longest_fetch_delay_msg(
+ struct cras_server_metrics_message *msg,
+ enum CRAS_SERVER_METRICS_TYPE type,
+ unsigned data)
+{
+ memset(msg, 0, sizeof(*msg));
+ msg->header.type = CRAS_MAIN_METRICS;
+ msg->header.length = sizeof(*msg);
+ msg->metrics_type = type;
+ msg->data = data;
+}
+
int cras_server_metrics_longest_fetch_delay(unsigned delay_msec)
{
struct cras_server_metrics_message msg;
int err;
- msg.header.type = CRAS_MAIN_METRICS;
- msg.header.length = sizeof(msg);
- msg.metrics_type = LONGEST_FETCH_DELAY;
- msg.data = delay_msec;
-
+ init_longest_fetch_delay_msg(&msg, LONGEST_FETCH_DELAY, delay_msec);
err = cras_main_message_send((struct cras_main_message *)&msg);
if (err < 0) {
syslog(LOG_ERR, "Failed to send metrics message");
@@ -43,7 +52,7 @@ int cras_server_metrics_longest_fetch_delay(unsigned delay_msec)
return 0;
}
-void metrics_longest_fetch_delay(unsigned delay_msec)
+static void metrics_longest_fetch_delay(unsigned delay_msec)
{
static const int fetch_delay_min_msec = 1;
static const int fetch_delay_max_msec = 10000;
@@ -56,7 +65,7 @@ void metrics_longest_fetch_delay(unsigned delay_msec)
fetch_delay_nbuckets);
}
-void handle_metrics_message(struct cras_main_message *msg, void *arg)
+static void handle_metrics_message(struct cras_main_message *msg, void *arg)
{
struct cras_server_metrics_message *metrics_msg =
(struct cras_server_metrics_message *)msg;
@@ -76,4 +85,4 @@ int cras_server_metrics_init() {
cras_main_message_add_handler(CRAS_MAIN_METRICS,
handle_metrics_message, NULL);
return 0;
-} \ No newline at end of file
+}
diff --git a/cras/src/server/cras_system_state.c b/cras/src/server/cras_system_state.c
index 26e573f3..3483ebcb 100644
--- a/cras/src/server/cras_system_state.c
+++ b/cras/src/server/cras_system_state.c
@@ -3,17 +3,20 @@
* found in the LICENSE file.
*/
+#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
+#include <sys/mman.h>
#include <sys/param.h>
-#include <sys/shm.h>
#include <sys/stat.h>
#include <syslog.h>
#include "cras_alsa_card.h"
#include "cras_config.h"
#include "cras_device_blacklist.h"
+#include "cras_observer.h"
+#include "cras_shm.h"
#include "cras_system_state.h"
#include "cras_tm.h"
#include "cras_types.h"
@@ -28,17 +31,14 @@ struct card_list {
/* The system state.
* Members:
* exp_state - The exported system state shared with clients.
- * shm_key - Key for shm area of system_state struct.
- * shm_id - Id for shm area of system_state struct.
+ * shm_name - Name of posix shm region for exported state.
+ * shm_fd - fd for shm area of system_state struct.
+ * shm_fd_ro - fd for shm area of system_state struct, opened read-only.
+ * shm_size - Size of the shm area.
* device_config_dir - Directory of device configs where volume curves live.
+ * internal_ucm_suffix - The suffix to append to internal card name to
+ * control which ucm config file to load.
* device_blacklist - Blacklist of device the server will ignore.
- * volume_alert - Called when the system volume changes.
- * mute_alert - Called when the system mute state changes.
- * suspend_alert - Called when the audio suspend state changes.
- * capture_gain_alert - Called when the capture gain changes.
- * capture_mute_alert - Called when the capture mute changes.
- * volume_limits_alert - Called when the volume limits are changed.
- * active_streams_alert - Called when the number of active streams changes.
* cards - A list of active sound cards in the system.
* update_lock - Protects the update_count, as audio threads can update the
* stream count.
@@ -46,17 +46,13 @@ struct card_list {
*/
static struct {
struct cras_server_state *exp_state;
- key_t shm_key;
- int shm_id;
+ char shm_name[NAME_MAX];
+ int shm_fd;
+ int shm_fd_ro;
+ size_t shm_size;
const char *device_config_dir;
+ const char *internal_ucm_suffix;
struct cras_device_blacklist *device_blacklist;
- struct cras_alert *volume_alert;
- struct cras_alert *mute_alert;
- struct cras_alert *suspend_alert;
- struct cras_alert *capture_gain_alert;
- struct cras_alert *capture_mute_alert;
- struct cras_alert *volume_limits_alert;
- struct cras_alert *active_streams_alert;
struct card_list *cards;
pthread_mutex_t update_lock;
struct cras_tm *tm;
@@ -74,25 +70,26 @@ static struct {
void cras_system_state_init(const char *device_config_dir)
{
struct cras_server_state *exp_state;
- unsigned loops = 0;
int rc;
- /* Find an available shm key. */
- do {
- state.shm_key = getpid() + rand();
- state.shm_id = shmget(state.shm_key, sizeof(*exp_state),
- IPC_CREAT | IPC_EXCL | 0640);
- } while (state.shm_id < 0 && loops++ < 100);
- if (state.shm_id < 0) {
- syslog(LOG_ERR, "Fatal: system state can't shmget");
- exit(state.shm_id);
- }
+ state.shm_size = sizeof(*exp_state);
+
+ snprintf(state.shm_name, sizeof(state.shm_name), "/cras-%d", getpid());
+ state.shm_fd = cras_shm_open_rw(state.shm_name, state.shm_size);
+ if (state.shm_fd < 0)
+ exit(state.shm_fd);
- exp_state = shmat(state.shm_id, NULL, 0);
- if (exp_state == (void *)-1) {
- syslog(LOG_ERR, "Fatal: system state can't shmat");
+ /* mmap shm. */
+ exp_state = mmap(NULL, state.shm_size,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ state.shm_fd, 0);
+ if (exp_state == (struct cras_server_state *)-1)
exit(-ENOMEM);
- }
+
+ /* Open a read-only copy to dup and pass to clients. */
+ state.shm_fd_ro = cras_shm_reopen_ro(state.shm_name, state.shm_fd);
+ if (state.shm_fd_ro < 0)
+ exit(state.shm_fd_ro);
/* Initial system state. */
exp_state->state_version = CRAS_SERVER_STATE_VERSION;
@@ -101,6 +98,7 @@ void cras_system_state_init(const char *device_config_dir)
exp_state->mute_locked = 0;
exp_state->suspended = 0;
exp_state->capture_gain = DEFAULT_CAPTURE_GAIN;
+ exp_state->capture_gain_target = DEFAULT_CAPTURE_GAIN;
exp_state->capture_mute = 0;
exp_state->capture_mute_locked = 0;
exp_state->min_volume_dBFS = DEFAULT_MIN_VOLUME_DBFS;
@@ -121,15 +119,7 @@ void cras_system_state_init(const char *device_config_dir)
* Device blacklist is common to all boards so we do not need
* to change device blacklist at run time. */
state.device_config_dir = device_config_dir;
-
- /* Initialize alerts. */
- state.volume_alert = cras_alert_create(NULL);
- state.mute_alert = cras_alert_create(NULL);
- state.suspend_alert = cras_alert_create(NULL);
- state.capture_gain_alert = cras_alert_create(NULL);
- state.capture_mute_alert = cras_alert_create(NULL);
- state.volume_limits_alert = cras_alert_create(NULL);
- state.active_streams_alert = cras_alert_create(NULL);
+ state.internal_ucm_suffix = NULL;
state.tm = cras_tm_init();
if (!state.tm) {
@@ -142,6 +132,11 @@ void cras_system_state_init(const char *device_config_dir)
cras_device_blacklist_create(CRAS_CONFIG_FILE_DIR);
}
+void cras_system_state_set_internal_ucm_suffix(const char *internal_ucm_suffix)
+{
+ state.internal_ucm_suffix = internal_ucm_suffix;
+}
+
void cras_system_state_deinit()
{
/* Free any resources used. This prevents unit tests from leaking. */
@@ -151,26 +146,12 @@ void cras_system_state_deinit()
cras_tm_deinit(state.tm);
if (state.exp_state) {
- shmdt(state.exp_state);
- shmctl(state.shm_id, IPC_RMID, (void *)state.exp_state);
+ munmap(state.exp_state, state.shm_size);
+ cras_shm_close_unlink(state.shm_name, state.shm_fd);
+ if (state.shm_fd_ro != state.shm_fd)
+ close(state.shm_fd_ro);
}
- cras_alert_destroy(state.volume_alert);
- cras_alert_destroy(state.mute_alert);
- cras_alert_destroy(state.suspend_alert);
- cras_alert_destroy(state.capture_gain_alert);
- cras_alert_destroy(state.capture_mute_alert);
- cras_alert_destroy(state.volume_limits_alert);
- cras_alert_destroy(state.active_streams_alert);
-
- state.volume_alert = NULL;
- state.mute_alert = NULL;
- state.suspend_alert = NULL;
- state.capture_gain_alert = NULL;
- state.capture_mute_alert = NULL;
- state.volume_limits_alert = NULL;
- state.active_streams_alert = NULL;
-
pthread_mutex_destroy(&state.update_lock);
}
@@ -180,7 +161,7 @@ void cras_system_set_volume(size_t volume)
syslog(LOG_DEBUG, "system volume set out of range %zu", volume);
state.exp_state->volume = MIN(volume, CRAS_MAX_SYSTEM_VOLUME);
- cras_alert_pending(state.volume_alert);
+ cras_observer_notify_output_volume(state.exp_state->volume);
}
size_t cras_system_get_volume()
@@ -188,21 +169,14 @@ size_t cras_system_get_volume()
return state.exp_state->volume;
}
-int cras_system_register_volume_changed_cb(cras_alert_cb cb, void *arg)
-{
- return cras_alert_add_callback(state.volume_alert, cb, arg);
-}
-
-int cras_system_remove_volume_changed_cb(cras_alert_cb cb, void *arg)
-{
- return cras_alert_rm_callback(state.volume_alert, cb, arg);
-}
-
void cras_system_set_capture_gain(long gain)
{
- state.exp_state->capture_gain =
- MAX(gain, state.exp_state->min_capture_gain);
- cras_alert_pending(state.capture_gain_alert);
+ /* Adjust targeted gain to be in supported range. */
+ state.exp_state->capture_gain_target = gain;
+ gain = MAX(gain, state.exp_state->min_capture_gain);
+ gain = MIN(gain, state.exp_state->max_capture_gain);
+ state.exp_state->capture_gain = gain;
+ cras_observer_notify_capture_gain(state.exp_state->capture_gain);
}
long cras_system_get_capture_gain()
@@ -210,20 +184,20 @@ long cras_system_get_capture_gain()
return state.exp_state->capture_gain;
}
-int cras_system_register_capture_gain_changed_cb(cras_alert_cb cb, void *arg)
-{
- return cras_alert_add_callback(state.capture_gain_alert, cb, arg);
-}
-
-int cras_system_remove_capture_gain_changed_cb(cras_alert_cb cb, void *arg)
+void cras_system_notify_mute(void)
{
- return cras_alert_rm_callback(state.capture_gain_alert, cb, arg);
+ cras_observer_notify_output_mute(state.exp_state->mute,
+ state.exp_state->user_mute,
+ state.exp_state->mute_locked);
}
void cras_system_set_user_mute(int mute)
{
+ if (state.exp_state->user_mute == !!mute)
+ return;
+
state.exp_state->user_mute = !!mute;
- cras_alert_pending(state.mute_alert);
+ cras_system_notify_mute();
}
void cras_system_set_mute(int mute)
@@ -231,16 +205,20 @@ void cras_system_set_mute(int mute)
if (state.exp_state->mute_locked)
return;
+ if (state.exp_state->mute == !!mute)
+ return;
+
state.exp_state->mute = !!mute;
- cras_alert_pending(state.mute_alert);
+ cras_system_notify_mute();
}
void cras_system_set_mute_locked(int locked)
{
- state.exp_state->mute_locked = !!locked;
+ if (state.exp_state->mute_locked == !!locked)
+ return;
- if (!state.exp_state->mute_locked)
- cras_alert_pending(state.mute_alert);
+ state.exp_state->mute_locked = !!locked;
+ cras_system_notify_mute();
}
int cras_system_get_mute()
@@ -263,14 +241,10 @@ int cras_system_get_mute_locked()
return state.exp_state->mute_locked;
}
-int cras_system_register_mute_changed_cb(cras_alert_cb cb, void *arg)
+void cras_system_notify_capture_mute(void)
{
- return cras_alert_add_callback(state.mute_alert, cb, arg);
-}
-
-int cras_system_remove_mute_changed_cb(cras_alert_cb cb, void *arg)
-{
- return cras_alert_rm_callback(state.mute_alert, cb, arg);
+ cras_observer_notify_capture_mute(state.exp_state->capture_mute,
+ state.exp_state->capture_mute_locked);
}
void cras_system_set_capture_mute(int mute)
@@ -279,15 +253,13 @@ void cras_system_set_capture_mute(int mute)
return;
state.exp_state->capture_mute = !!mute;
- cras_alert_pending(state.capture_mute_alert);
+ cras_system_notify_capture_mute();
}
void cras_system_set_capture_mute_locked(int locked)
{
state.exp_state->capture_mute_locked = !!locked;
-
- if (!state.exp_state->capture_mute_locked)
- cras_alert_pending(state.capture_mute_alert);
+ cras_system_notify_capture_mute();
}
int cras_system_get_capture_mute()
@@ -300,16 +272,6 @@ int cras_system_get_capture_mute_locked()
return state.exp_state->capture_mute_locked;
}
-int cras_system_register_capture_mute_changed_cb(cras_alert_cb cb, void *arg)
-{
- return cras_alert_add_callback(state.capture_mute_alert, cb, arg);
-}
-
-int cras_system_remove_capture_mute_changed_cb(cras_alert_cb cb, void *arg)
-{
- return cras_alert_rm_callback(state.capture_mute_alert, cb, arg);
-}
-
int cras_system_get_suspended()
{
return state.exp_state->suspended;
@@ -318,24 +280,13 @@ int cras_system_get_suspended()
void cras_system_set_suspended(int suspended)
{
state.exp_state->suspended = suspended;
- cras_alert_pending(state.suspend_alert);
-}
-
-int cras_system_register_suspend_cb(cras_alert_cb cb, void *arg)
-{
- return cras_alert_add_callback(state.suspend_alert, cb, arg);
-}
-
-int cras_system_remove_suspend_cb(cras_alert_cb cb, void *arg)
-{
- return cras_alert_rm_callback(state.suspend_alert, cb, arg);
+ cras_observer_notify_suspend_changed(suspended);
}
void cras_system_set_volume_limits(long min, long max)
{
state.exp_state->min_volume_dBFS = min;
state.exp_state->max_volume_dBFS = max;
- cras_alert_pending(state.volume_limits_alert);
}
long cras_system_get_min_volume()
@@ -348,21 +299,12 @@ long cras_system_get_max_volume()
return state.exp_state->max_volume_dBFS;
}
-int cras_system_register_volume_limits_changed_cb(cras_alert_cb cb, void *arg)
-{
- return cras_alert_add_callback(state.volume_limits_alert, cb, arg);
-}
-
-int cras_system_remove_volume_limits_changed_cb(cras_alert_cb cb, void *arg)
-{
- return cras_alert_rm_callback(state.volume_limits_alert, cb, arg);
-}
-
void cras_system_set_capture_gain_limits(long min, long max)
{
state.exp_state->min_capture_gain = MAX(min, DEFAULT_MIN_CAPTURE_GAIN);
state.exp_state->max_capture_gain = max;
- cras_alert_pending(state.volume_limits_alert);
+ /* Re-apply target gain subjected to the new supported range. */
+ cras_system_set_capture_gain(state.exp_state->capture_gain_target);
}
long cras_system_get_min_capture_gain()
@@ -390,9 +332,13 @@ int cras_system_add_alsa_card(struct cras_alsa_card_info *alsa_card_info)
if (card_index == cras_alsa_card_get_index(card->card))
return -EINVAL;
}
- alsa_card = cras_alsa_card_create(alsa_card_info,
- state.device_config_dir,
- state.device_blacklist);
+ alsa_card = cras_alsa_card_create(
+ alsa_card_info,
+ state.device_config_dir,
+ state.device_blacklist,
+ (alsa_card_info->card_type == ALSA_CARD_TYPE_INTERNAL)
+ ? state.internal_ucm_suffix
+ : NULL);
if (alsa_card == NULL)
return -ENOMEM;
card = calloc(1, sizeof(*card));
@@ -472,7 +418,8 @@ void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction)
s->num_streams_attached++;
cras_system_state_update_complete();
- cras_alert_pending(state.active_streams_alert);
+ cras_observer_notify_num_active_streams(
+ direction, s->num_active_streams[direction]);
}
void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction)
@@ -496,7 +443,8 @@ void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction)
s->num_active_streams[direction]--;
cras_system_state_update_complete();
- cras_alert_pending(state.active_streams_alert);
+ cras_observer_notify_num_active_streams(
+ direction, s->num_active_streams[direction]);
}
unsigned cras_system_state_get_active_streams()
@@ -514,16 +462,6 @@ unsigned cras_system_state_get_active_streams_by_direction(
return state.exp_state->num_active_streams[direction];
}
-int cras_system_register_active_streams_changed_cb(cras_alert_cb cb, void *arg)
-{
- return cras_alert_add_callback(state.active_streams_alert, cb, arg);
-}
-
-int cras_system_remove_active_streams_changed_cb(cras_alert_cb cb, void *arg)
-{
- return cras_alert_rm_callback(state.active_streams_alert, cb, arg);
-}
-
void cras_system_state_get_last_stream_active_time(struct cras_timespec *ts)
{
*ts = state.exp_state->last_active_stream_time;
@@ -575,9 +513,9 @@ struct cras_server_state *cras_system_state_get_no_lock()
return state.exp_state;
}
-key_t cras_sys_state_shm_key()
+key_t cras_sys_state_shm_fd()
{
- return state.shm_key;
+ return state.shm_fd_ro;
}
struct cras_tm *cras_system_state_get_tm()
diff --git a/cras/src/server/cras_system_state.h b/cras/src/server/cras_system_state.h
index 71dd9ec7..f0bb98ab 100644
--- a/cras/src/server/cras_system_state.h
+++ b/cras/src/server/cras_system_state.h
@@ -15,7 +15,6 @@
#include <stddef.h>
-#include "cras_alert.h"
#include "cras_types.h"
#define CRAS_MAX_SYSTEM_VOLUME 100
@@ -36,46 +35,19 @@ struct cras_tm;
void cras_system_state_init(const char *device_config_dir);
void cras_system_state_deinit();
+/* Sets the suffix string to control which UCM config fo load. */
+void cras_system_state_set_internal_ucm_suffix(const char *internal_ucm_suffix);
+
/* Sets the system volume. Will be applied by the active device. */
void cras_system_set_volume(size_t volume);
/* Gets the current system volume. */
size_t cras_system_get_volume();
-/* Adds a callback to call when the volume changes.
- * Args:
- * cb - Function to call when volume changes.
- * arg - Value to pass back to callback.
- */
-int cras_system_register_volume_changed_cb(cras_alert_cb cb, void *arg);
-
-/* Removes a callback to call when the volume changes. Only removes the entry
- * if both cb and arg match the values passed to the register function.
- * Args:
- * cb - Function to call when volume changes.
- * arg - Value to passed back to callback.
- */
-int cras_system_remove_volume_changed_cb(cras_alert_cb cb, void *arg);
-
/* Sets the system capture volume. Will be applied by the active device. */
void cras_system_set_capture_gain(long gain);
/* Gets the current system capture volume. */
long cras_system_get_capture_gain();
-/* Adds a callback to call when the capture volume changes.
- * Args:
- * cb - Function to call when capture volume changes.
- * arg - Value to pass back to callback.
- */
-int cras_system_register_capture_gain_changed_cb(cras_alert_cb cb, void *arg);
-
-/* Removes a callback to call when the capture volume changes. Only removes the
- * entry if both cb and arg match the values passed to the register function.
- * Args:
- * cb - Function to call when capture volume changes.
- * arg - Value to passed back to callback.
- */
-int cras_system_remove_capture_gain_changed_cb(cras_alert_cb cb, void *arg);
-
/* Sets if the system is muted by the user. */
void cras_system_set_user_mute(int muted);
/* Sets if the system is muted for . */
@@ -100,27 +72,6 @@ int cras_system_get_suspended();
*/
void cras_system_set_suspended(int suspend);
-/* Adds a callback to call when the suspend state changes. */
-int cras_system_register_suspend_cb(cras_alert_cb cb, void *arg);
-
-/* Removes a callback to call when the suspend state changes. */
-int cras_system_remove_suspend_cb(cras_alert_cb cb, void *arg);
-
-/* Adds a callback to call when the mute state changes.
- * Args:
- * cb - Function to call when mute state changes.
- * arg - Value to pass back to callback.
- */
-int cras_system_register_mute_changed_cb(cras_alert_cb cb, void *arg);
-
-/* Removes a callback to call when the mute state changes. Only removes the
- * entry if both cb and arg match the values passed to the register function.
- * Args:
- * cb - Function to call when volume changes.
- * arg - Value to passed back to callback.
- */
-int cras_system_remove_mute_changed_cb(cras_alert_cb cb, void *arg);
-
/* Sets if the system capture path is muted or not. */
void cras_system_set_capture_mute(int muted);
/* Sets if the system capture path muting is locked or not. */
@@ -130,22 +81,6 @@ int cras_system_get_capture_mute();
/* Gets if the system capture path muting is locked or not. */
int cras_system_get_capture_mute_locked();
-/* Adds a callback to call when the capture mute state changes.
- * Args:
- * cb - Function to call when the capture mute state changes.
- * arg - Value to pass back to callback.
- */
-int cras_system_register_capture_mute_changed_cb(cras_alert_cb cb, void *arg);
-
-/* Removes a callback to call when the capture mute state changes. Only removes
- * the entry if both cb and arg match the values passed to the register
- * function.
- * Args:
- * cb - Function to call when volume changes.
- * arg - Value to passed back to callback.
- */
-int cras_system_remove_capture_mute_changed_cb(cras_alert_cb cb, void *arg);
-
/* Sets the value in dB of the MAX and MIN volume settings. This will allow
* clients to query what range of control is available. Both arguments are
* specified as dB * 100.
@@ -159,22 +94,6 @@ long cras_system_get_min_volume();
/* Returns the dB value when volume = CRAS_MAX_SYSTEM_VOLUME, in dB * 100. */
long cras_system_get_max_volume();
-/* Adds a callback to call when the volume limits change.
- * Args:
- * cb - Function to call when there is a change.
- * arg - Value to pass back to callback.
- */
-int cras_system_register_volume_limits_changed_cb(cras_alert_cb cb, void *arg);
-
-/* Removes a callback to call when the volume limits change. Only removes
- * the entry if both cb and arg match the values passed to the register
- * function.
- * Args:
- * cb - Function to call when there is a change.
- * arg - Value to passed back to callback.
- */
-int cras_system_remove_volume_limits_changed_cb(cras_alert_cb cb, void *arg);
-
/* Sets the limits in dB * 100 of the MAX and MIN capture gain. This will allow
* clients to query what range of control is available. Both arguments are
* specified as dB * 100.
@@ -282,22 +201,6 @@ unsigned cras_system_state_get_active_streams();
unsigned cras_system_state_get_active_streams_by_direction(
enum CRAS_STREAM_DIRECTION direction);
-/* Adds a callback to call when the number of active streams changes.
- * Args:
- * cb - Function to call when there is a change.
- * arg - Value to pass back to callback.
- */
-int cras_system_register_active_streams_changed_cb(cras_alert_cb cb, void *arg);
-
-/* Removes a callback to call when the number of active streams changes.
- * Only removes the entry if both cb and arg match the values passed to the
- * register function.
- * Args:
- * cb - Function to call when there is a change.
- * arg - Value to passed back to callback.
- */
-int cras_system_remove_active_streams_changed_cb(cras_alert_cb cb, void *arg);
-
/* Fills ts with the time the last stream was removed from the system, the time
* the stream count went to zero.
*/
@@ -350,8 +253,8 @@ void cras_system_state_update_complete();
* log. Don't add calls to this function. */
struct cras_server_state *cras_system_state_get_no_lock();
-/* Returns the shm key for the server_state structure. */
-key_t cras_sys_state_shm_key();
+/* Returns the shm fd for the server_state structure. */
+key_t cras_sys_state_shm_fd();
/* Returns the timer manager. */
struct cras_tm *cras_system_state_get_tm();
diff --git a/cras/src/server/cras_tm.c b/cras/src/server/cras_tm.c
index 525d7c46..2eb9fe18 100644
--- a/cras/src/server/cras_tm.c
+++ b/cras/src/server/cras_tm.c
@@ -127,13 +127,24 @@ int cras_tm_get_next_timeout(const struct cras_tm *tm, struct timespec *ts)
void cras_tm_call_callbacks(struct cras_tm *tm)
{
struct timespec now;
- struct cras_timer *t;
+ struct cras_timer *t, *next;
clock_gettime(CLOCK_MONOTONIC_RAW, &now);
- DL_FOREACH(tm->timers, t)
+ /* Don't use DL_FOREACH to iterate timers because in each loop the
+ * next timer pointer is stored for later access but it could be
+ * cancelled and freed in current timer's callback causing invalid
+ * memory access. */
+ t = tm->timers;
+ while (t) {
+ next = t->next;
if (timespec_sooner(&t->ts, &now)) {
t->cb(t, t->cb_data);
+ /* Update next timer because it could have been modified
+ * in t->cb(). */
+ next = t->next;
cras_tm_cancel_timer(tm, t);
}
+ t = next;
+ }
}
diff --git a/cras/src/server/cras_udev.c b/cras/src/server/cras_udev.c
index f95b21b1..2ba1fcd1 100644
--- a/cras/src/server/cras_udev.c
+++ b/cras/src/server/cras_udev.c
@@ -252,24 +252,33 @@ static void fill_usb_card_info(struct cras_alsa_card_info *card_info,
struct udev_device *dev)
{
const char *sysattr;
- dev = udev_device_get_parent_with_subsystem_devtype(dev,
- "usb",
- "usb_device");
- if (!dev)
+ struct udev_device *parent_dev =
+ udev_device_get_parent_with_subsystem_devtype(dev,
+ "usb",
+ "usb_device");
+ if (!parent_dev)
return;
- sysattr = udev_device_get_sysattr_value(dev, "idVendor");
+ sysattr = udev_device_get_sysattr_value(parent_dev, "idVendor");
if (sysattr)
card_info->usb_vendor_id = strtol(sysattr, NULL, 16);
- sysattr = udev_device_get_sysattr_value(dev, "idProduct");
+ sysattr = udev_device_get_sysattr_value(parent_dev, "idProduct");
if (sysattr)
card_info->usb_product_id = strtol(sysattr, NULL, 16);
+ sysattr = udev_device_get_sysattr_value(parent_dev, "serial");
+ if (sysattr) {
+ strncpy(card_info->usb_serial_number, sysattr,
+ USB_SERIAL_NUMBER_BUFFER_SIZE - 1);
+ card_info->usb_serial_number[USB_SERIAL_NUMBER_BUFFER_SIZE - 1]
+ = '\0';
+ }
- card_info->usb_desc_checksum = calculate_desc_checksum(dev);
+ card_info->usb_desc_checksum = calculate_desc_checksum(parent_dev);
- syslog(LOG_ERR, "USB card: vendor:%04x, product:%04x, checksum:%08x",
+ syslog(LOG_ERR, "USB card: vendor:%04x, product:%04x, serial num:%s, "
+ "checksum:%08x",
card_info->usb_vendor_id, card_info->usb_product_id,
- card_info->usb_desc_checksum);
+ card_info->usb_serial_number, card_info->usb_desc_checksum);
}
static void device_add_alsa(struct udev_device *dev,
@@ -278,6 +287,7 @@ static void device_add_alsa(struct udev_device *dev,
unsigned internal)
{
struct cras_alsa_card_info card_info;
+ memset(&card_info, 0, sizeof(card_info));
udev_delay_for_alsa();
card_info.card_index = card;
diff --git a/cras/src/server/cras_utf8.c b/cras/src/server/cras_utf8.c
new file mode 100644
index 00000000..d3780d36
--- /dev/null
+++ b/cras/src/server/cras_utf8.c
@@ -0,0 +1,198 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef CRAS_DBUS
+#include <dbus/dbus.h>
+#endif
+
+#include "cras_utf8.h"
+#include "cras_util.h"
+
+static const uint8_t kUTF8ByteOrderMask[3] = { 0xef, 0xbb, 0xbf };
+
+typedef struct u8range {
+ uint8_t min;
+ uint8_t max;
+} u8range_t;
+
+static const u8range_t kUTF8TwoByteSeq[] = {
+ { 0xc2, 0xdf },
+ { 0x80, 0xbf },
+ { 0, 0 }
+};
+
+static const u8range_t kUTF8ByteSeqE0[] = {
+ { 0xe0, 0xe0 },
+ { 0xa0, 0xbf },
+ { 0x80, 0xbf },
+ { 0, 0 }
+};
+
+static const u8range_t kUTF8ByteSeqE1EC[] = {
+ { 0xe1, 0xec },
+ { 0x80, 0xbf },
+ { 0x80, 0xbf },
+ { 0, 0 }
+};
+
+static const u8range_t kUTF8ByteSeqED[] = {
+ { 0xed, 0xed },
+ { 0x80, 0x9f },
+ { 0x80, 0xbf },
+ { 0, 0 }
+};
+
+static const u8range_t kUTF8ByteSeqEEEF[] = {
+ { 0xee, 0xef },
+ { 0x80, 0xbf },
+ { 0x80, 0xbf },
+ { 0, 0 }
+};
+
+static const u8range_t kUTF8ByteSeqF0[] = {
+ { 0xf0, 0xf0 },
+ { 0x90, 0xbf },
+ { 0x80, 0xbf },
+ { 0x80, 0xbf },
+ { 0, 0 }
+};
+
+static const u8range_t kUTF8ByteSeqF1F3[] = {
+ { 0xf1, 0xf3 },
+ { 0x80, 0xbf },
+ { 0x80, 0xbf },
+ { 0x80, 0xbf },
+ { 0, 0 }
+};
+
+static const u8range_t kUTF8ByteSeqF4[] = {
+ { 0xf4, 0xf4 },
+ { 0x80, 0x8f },
+ { 0x80, 0xbf },
+ { 0x80, 0xbf },
+ { 0, 0 }
+};
+
+static const u8range_t kUTF8NullRange[] = {
+ { 0, 0 }
+};
+
+typedef struct utf8seq {
+ const u8range_t *ranges;
+} utf8seq_t;
+
+static const utf8seq_t kUTF8Sequences[] = {
+ { kUTF8TwoByteSeq },
+ { kUTF8ByteSeqE0 },
+ { kUTF8ByteSeqE1EC },
+ { kUTF8ByteSeqED },
+ { kUTF8ByteSeqEEEF },
+ { kUTF8ByteSeqF0 },
+ { kUTF8ByteSeqF1F3 },
+ { kUTF8ByteSeqF4 },
+ { kUTF8NullRange }
+};
+
+int valid_utf8_string(const char *string, size_t *bad_pos)
+{
+ int bom_chars = 0;
+ uint8_t byte;
+ const char *pos = string;
+ int ret = 1;
+ const utf8seq_t *seq = NULL;
+ const u8range_t *range = NULL;
+
+ if (!pos) {
+ ret = 0;
+ goto error;
+ }
+
+ while ((byte = (uint8_t)*(pos++))) {
+ if (!range || range->min == 0) {
+ if (byte < 128) {
+ /* Ascii character. */
+ continue;
+ }
+
+ if (bom_chars < ARRAY_SIZE(kUTF8ByteOrderMask)) {
+ if (byte == kUTF8ByteOrderMask[bom_chars]) {
+ bom_chars++;
+ continue;
+ } else {
+ /* Characters not matching BOM.
+ * Rewind and assume that there is
+ * no BOM. */
+ bom_chars =
+ ARRAY_SIZE(kUTF8ByteOrderMask);
+ pos = string;
+ continue;
+ }
+ }
+
+ /* Find the matching sequence of characters by
+ * matching the first character in the sequence.
+ */
+ seq = kUTF8Sequences;
+ while (seq->ranges->min != 0) {
+ if (byte >= seq->ranges->min &&
+ byte <= seq->ranges->max) {
+ /* Matching sequence. */
+ break;
+ }
+ seq++;
+ }
+
+ if (seq->ranges->min == 0) {
+ /* Could not find a matching sequence. */
+ ret = 0;
+ goto error;
+ }
+
+ /* Found the appropriate sequence. */
+ range = seq->ranges + 1;
+ continue;
+ }
+
+ if (byte >= range->min && byte <= range->max) {
+ range++;
+ continue;
+ }
+
+ /* This character doesn't belong in UTF8. */
+ ret = 0;
+ goto error;
+ }
+
+ if (range && range->min != 0) {
+ /* Stopped in the middle of a sequence. */
+ ret = 0;
+ }
+
+error:
+ if (bad_pos)
+ *bad_pos = pos - string - 1;
+ return ret;
+}
+
+#ifdef CRAS_DBUS
+/* Use the DBus implementation if available to ensure that the UTF-8
+ * sequences match those expected by the DBus implementation. */
+
+int is_utf8_string(const char *string)
+{
+ return !!dbus_validate_utf8(string, NULL);
+}
+
+#else
+
+int is_utf8_string (const char *string) {
+ return valid_utf8_string(string, NULL);
+}
+
+#endif
diff --git a/cras/src/server/cras_utf8.h b/cras/src/server/cras_utf8.h
new file mode 100644
index 00000000..ab0fda0c
--- /dev/null
+++ b/cras/src/server/cras_utf8.h
@@ -0,0 +1,36 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <sys/types.h>
+
+/* Checks if a string is valid UTF-8.
+ *
+ * Supports 1 to 4 character UTF-8 sequences. Passes tests here:
+ * https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+ *
+ * Exceptions: The following unicode non-characters are allowed:
+ * U+FFFE, U+FFFF, U+FDD0 - U+FDEF, U+nFFFE (n = 1 - 10),
+ * U+nFFFD (n = 1 - 10).
+ *
+ * Args:
+ * string[in] - a string.
+ * bad_pos[out] - position of the first bad character.
+ *
+ * Returns:
+ * 1 if it is a vlid utf-8 string. 0 otherwise.
+ * bad_pos contains the strlen() of the string if it is
+ * valid.
+ */
+int valid_utf8_string(const char *string, size_t *bad_pos);
+
+/* Checks if a string is a valid utf-8 string.
+ *
+ * Args:
+ * string[in] - a string.
+ *
+ * Returns:
+ * 1 if it is a valid utf-8 string. 0 otherwise.
+ */
+int is_utf8_string(const char* string);
diff --git a/cras/src/server/dev_stream.c b/cras/src/server/dev_stream.c
index a80b948e..4d893cc6 100644
--- a/cras/src/server/dev_stream.c
+++ b/cras/src/server/dev_stream.c
@@ -23,19 +23,31 @@ static const unsigned int capture_extra_sleep_frames = 20;
*/
static const int coarse_rate_adjust_step = 3;
+/*
+ * Allow capture callback to fire this much earlier than the scheduled
+ * next_cb_ts to avoid an extra wake of audio thread.
+ */
+static const struct timespec capture_callback_fuzz_ts = {
+ .tv_sec = 0,
+ .tv_nsec = 1000000, /* 1 ms. */
+};
+
struct dev_stream *dev_stream_create(struct cras_rstream *stream,
unsigned int dev_id,
const struct cras_audio_format *dev_fmt,
- void *dev_ptr)
+ void *dev_ptr,
+ struct timespec *cb_ts)
{
struct dev_stream *out;
struct cras_audio_format *stream_fmt = &stream->format;
int rc = 0;
- unsigned int max_frames;
+ unsigned int max_frames, dev_frames, buf_bytes;
+ const struct cras_audio_format *ofmt;
out = calloc(1, sizeof(*out));
out->dev_id = dev_id;
out->stream = stream;
+ out->dev_rate = dev_fmt->frame_rate;
if (stream->direction == CRAS_STREAM_OUTPUT) {
max_frames = MAX(stream->buffer_frames,
@@ -63,33 +75,28 @@ struct dev_stream *dev_stream_create(struct cras_rstream *stream,
return NULL;
}
- if (out->conv) {
- unsigned int dev_frames;
- unsigned int buf_bytes;
- const struct cras_audio_format *ofmt =
- cras_fmt_conv_out_format(out->conv);
-
- dev_frames = (stream->direction == CRAS_STREAM_OUTPUT)
- ? cras_fmt_conv_in_frames_to_out(out->conv,
- stream->buffer_frames)
- : cras_fmt_conv_out_frames_to_in(out->conv,
- stream->buffer_frames);
-
- out->conv_buffer_size_frames = 2 * MAX(dev_frames,
- stream->buffer_frames);
-
- /* Create conversion buffer and area using the output format
- * of the format converter. Note that this format might not be
- * identical to stream_fmt for capture. */
- buf_bytes = out->conv_buffer_size_frames * cras_get_format_bytes(ofmt);
- out->conv_buffer = byte_buffer_create(buf_bytes);
- out->conv_area = cras_audio_area_create(ofmt->num_channels);
- }
+ ofmt = cras_fmt_conv_out_format(out->conv);
+
+ dev_frames = (stream->direction == CRAS_STREAM_OUTPUT)
+ ? cras_fmt_conv_in_frames_to_out(out->conv,
+ stream->buffer_frames)
+ : cras_fmt_conv_out_frames_to_in(out->conv,
+ stream->buffer_frames);
+
+ out->conv_buffer_size_frames = 2 * MAX(dev_frames,
+ stream->buffer_frames);
+
+ /* Create conversion buffer and area using the output format
+ * of the format converter. Note that this format might not be
+ * identical to stream_fmt for capture. */
+ buf_bytes = out->conv_buffer_size_frames * cras_get_format_bytes(ofmt);
+ out->conv_buffer = byte_buffer_create(buf_bytes);
+ out->conv_area = cras_audio_area_create(ofmt->num_channels);
cras_frames_to_time(cras_rstream_get_cb_threshold(stream),
stream_fmt->frame_rate,
&stream->sleep_interval_ts);
- clock_gettime(CLOCK_MONOTONIC_RAW, &stream->next_cb_ts);
+ stream->next_cb_ts = *cb_ts;
if (stream->direction != CRAS_STREAM_OUTPUT) {
struct timespec extra_sleep;
@@ -255,7 +262,7 @@ static unsigned int capture_with_fmt_conv(struct dev_stream *dev_stream,
static unsigned int capture_copy_converted_to_stream(
struct dev_stream *dev_stream,
struct cras_rstream *rstream,
- unsigned int dev_index)
+ float software_gain_scaler)
{
struct cras_audio_shm *shm;
uint8_t *stream_samples;
@@ -306,7 +313,8 @@ static unsigned int capture_copy_converted_to_stream(
cras_audio_area_copy(rstream->audio_area, offset,
&rstream->format,
- dev_stream->conv_area, 0, 1);
+ dev_stream->conv_area, 0,
+ software_gain_scaler);
buf_increment_read(dev_stream->conv_buffer,
write_frames * frame_bytes);
@@ -326,7 +334,7 @@ static unsigned int capture_copy_converted_to_stream(
unsigned int dev_stream_capture(struct dev_stream *dev_stream,
const struct cras_audio_area *area,
unsigned int area_offset,
- unsigned int dev_idx)
+ float software_gain_scaler)
{
struct cras_rstream *rstream = dev_stream->stream;
struct cras_audio_shm *shm;
@@ -343,7 +351,8 @@ unsigned int dev_stream_capture(struct dev_stream *dev_stream,
dev_stream,
area->channels[0].buf + area_offset * format_bytes,
area->frames - area_offset);
- capture_copy_converted_to_stream(dev_stream, rstream, dev_idx);
+ capture_copy_converted_to_stream(dev_stream, rstream,
+ software_gain_scaler);
} else {
unsigned int offset =
cras_rstream_dev_offset(rstream, dev_stream->dev_id);
@@ -360,7 +369,9 @@ unsigned int dev_stream_capture(struct dev_stream *dev_stream,
nread = cras_audio_area_copy(rstream->audio_area, offset,
&rstream->format, area,
- area_offset, 1);
+ area_offset,
+ software_gain_scaler);
+
ATLOG(atlog, AUDIO_THREAD_CAPTURE_WRITE,
rstream->stream_id,
nread,
@@ -469,18 +480,32 @@ int dev_stream_playback_update_rstream(struct dev_stream *dev_stream)
return 0;
}
+static int late_enough_for_capture_callback(struct dev_stream *dev_stream)
+{
+ struct timespec now;
+ struct cras_rstream *rstream = dev_stream->stream;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+ add_timespecs(&now, &capture_callback_fuzz_ts);
+ return timespec_after(&now, &rstream->next_cb_ts);
+}
+
int dev_stream_capture_update_rstream(struct dev_stream *dev_stream)
{
struct cras_rstream *rstream = dev_stream->stream;
- unsigned int str_cb_threshold = cras_rstream_get_cb_threshold(rstream);
- unsigned int frames_ready = str_cb_threshold;
- struct timespec now;
+ unsigned int frames_ready = cras_rstream_get_cb_threshold(rstream);
+ int rc;
cras_rstream_update_input_write_pointer(rstream);
- /* If it isn't time for this stream then skip it. */
- clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+ /*
+ * For stream without BULK_AUDIO_OK flag, if it isn't time for
+ * this stream then skip it.
+ */
+ if (!(rstream->flags & BULK_AUDIO_OK) &&
+ !late_enough_for_capture_callback(dev_stream))
+ return 0;
+ /* If there is not enough data for one callback, skip it. */
if (!cras_rstream_input_level_met(rstream))
return 0;
@@ -493,7 +518,18 @@ int dev_stream_capture_update_rstream(struct dev_stream *dev_stream)
frames_ready,
rstream->shm.area->read_buf_idx);
- return cras_rstream_audio_ready(rstream, frames_ready);
+ rc = cras_rstream_audio_ready(rstream, frames_ready);
+
+ if (rc < 0)
+ return rc;
+
+ /* Update next callback time according to perfect schedule. */
+ add_timespecs(&rstream->next_cb_ts,
+ &rstream->sleep_interval_ts);
+ /* Reset schedule if the schedule is missed. */
+ check_next_wake_time(dev_stream);
+
+ return 0;
}
void cras_set_playback_timestamp(size_t frame_rate,
@@ -582,9 +618,7 @@ int dev_stream_request_playback_samples(struct dev_stream *dev_stream,
struct cras_rstream *rstream = dev_stream->stream;
int rc;
- cras_rstream_record_fetch_interval(dev_stream->stream, now);
-
- rc = cras_rstream_request_audio(dev_stream->stream);
+ rc = cras_rstream_request_audio(dev_stream->stream, now);
if (rc < 0)
return rc;
@@ -607,3 +641,98 @@ int dev_stream_poll_stream_fd(const struct dev_stream *dev_stream)
return stream->fd;
}
+
+/*
+ * Needed frames from this device such that written frames in shm meets
+ * cb_threshold.
+ */
+static int get_input_needed_frames(struct dev_stream *dev_stream,
+ unsigned int curr_level)
+{
+ struct cras_rstream *rstream = dev_stream->stream;
+ unsigned int rstream_level = cras_rstream_level(rstream);
+ unsigned int dev_offset = cras_rstream_dev_offset(
+ rstream, dev_stream->dev_id);
+ unsigned int needed_for_stream;
+
+ /*
+ * rstream_level + def_offset is the number of frames written to shm
+ * from this device.
+ */
+ if (rstream_level + dev_offset > rstream->cb_threshold) {
+ /* Enough frames from this device for this stream. */
+ return 0;
+ }
+
+ /*
+ * Needed number of frames in shm such that written frames in shm meets
+ * cb_threshold.
+ */
+ needed_for_stream = rstream->cb_threshold - rstream_level - dev_offset;
+
+ /* Convert the number of frames from stream format to device format. */
+ return cras_fmt_conv_out_frames_to_in(dev_stream->conv,
+ needed_for_stream);
+
+}
+
+/*
+ * Gets proper wake up time for an input stream. It considers both
+ * time for samples to reach one callback level, and the time for next callback.
+ */
+static int get_input_wake_time(struct dev_stream *dev_stream,
+ unsigned int curr_level,
+ struct timespec *level_tstamp,
+ struct timespec *wake_time_out)
+{
+ struct cras_rstream *rstream = dev_stream->stream;
+ struct timespec time_for_sample;
+ int needed_frames_from_device;
+
+ needed_frames_from_device = get_input_needed_frames(
+ dev_stream, curr_level);
+
+ /*
+ * If current frames in the device can provide needed amount for stream,
+ * there is no need to wait.
+ */
+ if (curr_level >= needed_frames_from_device)
+ needed_frames_from_device = 0;
+
+ else
+ needed_frames_from_device -= curr_level;
+
+ cras_frames_to_time(needed_frames_from_device,
+ dev_stream->dev_rate,
+ &time_for_sample);
+
+ add_timespecs(&time_for_sample, level_tstamp);
+
+ /* Select the time that is later so both sample and time conditions
+ * are met. */
+ if (timespec_after(&time_for_sample, &rstream->next_cb_ts)) {
+ *wake_time_out = time_for_sample;
+ } else {
+ *wake_time_out = rstream->next_cb_ts;
+ }
+
+ return 0;
+}
+
+int dev_stream_wake_time(struct dev_stream *dev_stream,
+ unsigned int curr_level,
+ struct timespec *level_tstamp,
+ struct timespec *wake_time_out)
+{
+ if (dev_stream->stream->direction == CRAS_STREAM_OUTPUT) {
+ /*
+ * TODO(cychiang) Implement the method for output stream.
+ * The logic should be similar to what
+ * get_next_stream_wake_from_list in audio_thread.c is doing.
+ */
+ return -EINVAL;
+ }
+
+ return get_input_wake_time(dev_stream, curr_level, level_tstamp,
+ wake_time_out);
+}
diff --git a/cras/src/server/dev_stream.h b/cras/src/server/dev_stream.h
index 8f68a672..c4af825c 100644
--- a/cras/src/server/dev_stream.h
+++ b/cras/src/server/dev_stream.h
@@ -27,7 +27,8 @@ struct cras_iodev;
* conv - Sample rate or format converter.
* conv_buffer - The buffer for converter if needed.
* conv_buffer_size_frames - Size of conv_buffer in frames.
- * skip_mix - Don't mix this next time streams are mixed.
+ * dev_rate - Sampling rate of device. This is set when dev_stream is
+ * created.
*/
struct dev_stream {
unsigned int dev_id;
@@ -36,14 +37,14 @@ struct dev_stream {
struct byte_buffer *conv_buffer;
struct cras_audio_area *conv_area;
unsigned int conv_buffer_size_frames;
- unsigned int skip_mix;
+ size_t dev_rate;
struct dev_stream *prev, *next;
};
struct dev_stream *dev_stream_create(struct cras_rstream *stream,
unsigned int dev_id,
const struct cras_audio_format *dev_fmt,
- void *dev_ptr);
+ void *dev_ptr, struct timespec *cb_ts);
void dev_stream_destroy(struct dev_stream *dev_stream);
/*
@@ -87,12 +88,12 @@ int dev_stream_mix(struct dev_stream *dev_stream,
* dev_stream - The struct holding the stream to mix to.
* area - The area to copy audio from.
* area_offset - The offset at which to start reading from area.
- * index - The index of the buffer to copy to the dev stream.
+ * software_gain_scaler - The software gain scaler.
*/
unsigned int dev_stream_capture(struct dev_stream *dev_stream,
const struct cras_audio_area *area,
unsigned int area_offset,
- unsigned int dev_index);
+ float software_gain_scaler);
/* Returns the number of iodevs this stream has attached to. */
int dev_stream_attached_devs(const struct dev_stream *dev_stream);
@@ -155,6 +156,22 @@ int dev_stream_request_playback_samples(struct dev_stream *dev_stream,
const struct timespec *now);
/*
+ * Gets the wake up time for a dev_stream.
+ * For an input stream, it considers both needed samples and proper time
+ * interval between each callbacks.
+ * Args:
+ * dev_stream[in]: The dev_stream to check wake up time.
+ * curr_level[in]: The current level of device.
+ * wake_time_out[out]: A timespec for wake up time.
+ * Returns:
+ * 0 on success; negative error code on failure.
+ */
+int dev_stream_wake_time(struct dev_stream *dev_stream,
+ unsigned int curr_level,
+ struct timespec *level_tstamp,
+ struct timespec *wake_time_out);
+
+/*
* Returns a non-negative fd if the fd is expecting a message and should be
* added to the list of descriptors to poll.
*/
diff --git a/cras/src/server/linear_resampler.c b/cras/src/server/linear_resampler.c
index 33b028b0..e014e57e 100644
--- a/cras/src/server/linear_resampler.c
+++ b/cras/src/server/linear_resampler.c
@@ -35,6 +35,8 @@ struct linear_resampler *linear_resampler_create(unsigned int num_channels,
struct linear_resampler *lr;
lr = (struct linear_resampler *)calloc(1, sizeof(*lr));
+ if (!lr)
+ return NULL;
lr->num_channels = num_channels;
lr->format_bytes = format_bytes;
diff --git a/cras/src/server/softvol_curve.c b/cras/src/server/softvol_curve.c
index f0256932..d98ca4c4 100644
--- a/cras/src/server/softvol_curve.c
+++ b/cras/src/server/softvol_curve.c
@@ -117,7 +117,6 @@ const float softvol_scalers[101] = {
float *softvol_build_from_curve(const struct cras_volume_curve *curve)
{
float *scalers;
- long max_dBFS, diff;
unsigned int volume;
if (!curve)
@@ -127,12 +126,16 @@ float *softvol_build_from_curve(const struct cras_volume_curve *curve)
if (!scalers)
return NULL;
- max_dBFS = curve->get_dBFS(curve, MAX_VOLUME);
- scalers[MAX_VOLUME] = 1.0;
-
- for (volume = 0; volume < MAX_VOLUME; volume++) {
- diff = curve->get_dBFS(curve, volume) - max_dBFS;
- scalers[volume] = convert_softvol_scaler_from_dB(diff);
+ /* When software volume is used, it is assumed all volume curve values
+ * are relative to 0 dBFS when converting to scale. If a positive dBFS
+ * value is specified in curve config, it will be treated as invalid
+ * and clip to 1.0 in scale.
+ */
+ for (volume = 0; volume <= MAX_VOLUME; volume++) {
+ scalers[volume] = convert_softvol_scaler_from_dB(
+ curve->get_dBFS(curve, volume));
+ if (scalers[volume] > 1.0)
+ scalers[volume] = 1.0;
}
return scalers;
diff --git a/cras/src/server/test_iodev.c b/cras/src/server/test_iodev.c
index 72a0f3d3..4955726e 100644
--- a/cras/src/server/test_iodev.c
+++ b/cras/src/server/test_iodev.c
@@ -36,7 +36,6 @@ static snd_pcm_format_t test_supported_formats[] = {
struct test_iodev {
struct cras_iodev base;
- int open;
int fd;
struct byte_buffer *audbuff;
unsigned int fmt_bytes;
@@ -46,19 +45,8 @@ struct test_iodev {
* iodev callbacks.
*/
-static int is_open(const struct cras_iodev *iodev)
-{
- struct test_iodev *testio = (struct test_iodev *)iodev;
-
- return testio->open;
-}
-
-static int dev_running(const struct cras_iodev *iodev)
-{
- return 1;
-}
-
-static int frames_queued(const struct cras_iodev *iodev)
+static int frames_queued(const struct cras_iodev *iodev,
+ struct timespec *tstamp)
{
struct test_iodev *testio = (struct test_iodev *)iodev;
int available;
@@ -66,6 +54,7 @@ static int frames_queued(const struct cras_iodev *iodev)
if (testio->fd < 0)
return 0;
ioctl(testio->fd, FIONREAD, &available);
+ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
return available / testio->fmt_bytes;
}
@@ -78,7 +67,6 @@ static int close_dev(struct cras_iodev *iodev)
{
struct test_iodev *testio = (struct test_iodev *)iodev;
- testio->open = 0;
byte_buffer_destroy(testio->audbuff);
testio->audbuff = NULL;
cras_iodev_free_audio_area(iodev);
@@ -93,7 +81,6 @@ static int open_dev(struct cras_iodev *iodev)
return -EINVAL;
cras_iodev_init_audio_area(iodev, iodev->format->num_channels);
- testio->open = 1;
testio->fmt_bytes = cras_get_format_bytes(iodev->format);
testio->audbuff = byte_buffer_create(TEST_BUFFER_SIZE *
testio->fmt_bytes);
@@ -161,14 +148,18 @@ static int get_buffer_fd_read(struct cras_iodev *iodev,
return nread;
}
-static void update_active_node(struct cras_iodev *iodev, unsigned node_idx)
+static void update_active_node(struct cras_iodev *iodev, unsigned node_idx,
+ unsigned dev_enabled)
{
}
static void play_file_as_hotword(struct test_iodev *testio, const char *path)
{
if (testio->fd >= 0) {
- audio_thread_rm_callback(testio->fd);
+ /* Remove audio thread callback from main thread. */
+ audio_thread_rm_callback_sync(
+ cras_iodev_list_get_audio_thread(),
+ testio->fd);
close(testio->fd);
}
@@ -204,7 +195,6 @@ struct cras_iodev *test_iodev_create(enum CRAS_STREAM_DIRECTION direction,
iodev->open_dev = open_dev;
iodev->close_dev = close_dev;
- iodev->is_open = is_open;
iodev->frames_queued = frames_queued;
iodev->delay_frames = delay_frames;
if (type == TEST_IODEV_HOTWORD)
@@ -212,7 +202,6 @@ struct cras_iodev *test_iodev_create(enum CRAS_STREAM_DIRECTION direction,
else
iodev->get_buffer = get_buffer;
iodev->put_buffer = put_buffer;
- iodev->dev_running = dev_running;
iodev->update_active_node = update_active_node;
/* Create a dummy ionode */
@@ -220,10 +209,12 @@ struct cras_iodev *test_iodev_create(enum CRAS_STREAM_DIRECTION direction,
node->dev = iodev;
node->plugged = 1;
if (type == TEST_IODEV_HOTWORD)
- node->type = CRAS_NODE_TYPE_AOKR;
+ node->type = CRAS_NODE_TYPE_HOTWORD;
else
node->type = CRAS_NODE_TYPE_UNKNOWN;
node->volume = 100;
+ node->software_volume_needed = 0;
+ node->max_software_gain = 0;
strcpy(node->name, "(default)");
cras_iodev_add_node(iodev, node);
cras_iodev_set_active_node(iodev, node);
@@ -267,7 +258,7 @@ void test_iodev_command(struct cras_iodev *iodev,
{
struct test_iodev *testio = (struct test_iodev *)iodev;
- if (!is_open(iodev))
+ if (!cras_iodev_is_open(iodev))
return;
switch (command) {
diff --git a/cras/src/tests/a2dp_iodev_unittest.cc b/cras/src/tests/a2dp_iodev_unittest.cc
index 88f058d5..6e8e3c77 100644
--- a/cras/src/tests/a2dp_iodev_unittest.cc
+++ b/cras/src/tests/a2dp_iodev_unittest.cc
@@ -20,7 +20,7 @@ extern "C" {
#define FAKE_OBJECT_PATH "/fake/obj/path"
-#define MAX_A2DP_ENCODE_CALLS 2
+#define MAX_A2DP_ENCODE_CALLS 8
#define MAX_A2DP_WRITE_CALLS 4
static struct cras_bt_transport *fake_transport;
@@ -108,7 +108,7 @@ TEST(A2dpIoInit, InitializeA2dpIodev) {
ResetStubData();
cras_bt_device_name_ret = NULL;
- iodev = a2dp_iodev_create(fake_transport, NULL);
+ iodev = a2dp_iodev_create(fake_transport);
ASSERT_NE(iodev, (void *)NULL);
ASSERT_EQ(iodev->direction, CRAS_STREAM_OUTPUT);
@@ -131,7 +131,7 @@ TEST(A2dpIoInit, InitializeA2dpIodev) {
cras_bt_device_name_ret = fake_device_name;
/* Assert iodev name matches the bt device's name */
- iodev = a2dp_iodev_create(fake_transport, NULL);
+ iodev = a2dp_iodev_create(fake_transport);
ASSERT_STREQ(fake_device_name, iodev->info.name);
a2dp_iodev_destroy(iodev);
@@ -143,7 +143,7 @@ TEST(A2dpIoInit, InitializeFail) {
ResetStubData();
init_a2dp_return_val = -1;
- iodev = a2dp_iodev_create(fake_transport, NULL);
+ iodev = a2dp_iodev_create(fake_transport);
ASSERT_EQ(iodev, (void *)NULL);
ASSERT_EQ(1, cras_bt_transport_configuration_called);
@@ -158,7 +158,7 @@ TEST(A2dpIoInit, OpenIodev) {
struct cras_iodev *iodev;
ResetStubData();
- iodev = a2dp_iodev_create(fake_transport, NULL);
+ iodev = a2dp_iodev_create(fake_transport);
iodev_set_format(iodev, &format);
iodev->open_dev(iodev);
@@ -180,7 +180,7 @@ TEST(A2dpIoInit, GetPutBuffer) {
unsigned frames;
ResetStubData();
- iodev = a2dp_iodev_create(fake_transport, NULL);
+ iodev = a2dp_iodev_create(fake_transport);
iodev_set_format(iodev, &format);
iodev->open_dev(iodev);
@@ -239,10 +239,11 @@ TEST(A2dpIoInit, GetPutBuffer) {
TEST(A2dpIoInif, FramesQueued) {
struct cras_iodev *iodev;
struct cras_audio_area *area;
+ struct timespec tstamp;
unsigned frames;
ResetStubData();
- iodev = a2dp_iodev_create(fake_transport, NULL);
+ iodev = a2dp_iodev_create(fake_transport);
iodev_set_format(iodev, &format);
time_now.tv_sec = 0;
@@ -267,7 +268,9 @@ TEST(A2dpIoInif, FramesQueued) {
time_now.tv_nsec = 1000000;
iodev->put_buffer(iodev, 300);
write_callback(write_callback_data);
- EXPECT_EQ(350, iodev->frames_queued(iodev));
+ EXPECT_EQ(350, iodev->frames_queued(iodev, &tstamp));
+ EXPECT_EQ(tstamp.tv_sec, time_now.tv_sec);
+ EXPECT_EQ(tstamp.tv_nsec, time_now.tv_nsec);
/* After writing another 200 frames, check for correct buffer level. */
time_now.tv_sec = 0;
@@ -277,9 +280,11 @@ TEST(A2dpIoInif, FramesQueued) {
a2dp_encode_processed_bytes_val[0] = 800;
write_callback(write_callback_data);
/* 1000000 nsec has passed, estimated queued frames adjusted by 44 */
- EXPECT_EQ(256, iodev->frames_queued(iodev));
+ EXPECT_EQ(256, iodev->frames_queued(iodev, &tstamp));
EXPECT_EQ(1200, pcm_buf_size_val[0]);
EXPECT_EQ(400, pcm_buf_size_val[1]);
+ EXPECT_EQ(tstamp.tv_sec, time_now.tv_sec);
+ EXPECT_EQ(tstamp.tv_nsec, time_now.tv_nsec);
/* Queued frames and new put buffer are all written */
a2dp_encode_processed_bytes_val[0] = 400;
@@ -295,7 +300,57 @@ TEST(A2dpIoInif, FramesQueued) {
a2dp_encode_processed_bytes_val[0] = 600;
iodev->put_buffer(iodev, 200);
EXPECT_EQ(1200, pcm_buf_size_val[0]);
- EXPECT_EQ(200, iodev->frames_queued(iodev));
+ EXPECT_EQ(200, iodev->frames_queued(iodev, &tstamp));
+ EXPECT_EQ(tstamp.tv_sec, time_now.tv_sec);
+ EXPECT_EQ(tstamp.tv_nsec, time_now.tv_nsec);
+}
+
+TEST(A2dpIo, FlushAtLowBufferLevel) {
+ struct cras_iodev *iodev;
+ struct cras_audio_area *area;
+ struct timespec tstamp;
+ unsigned frames;
+
+ ResetStubData();
+ iodev = a2dp_iodev_create(fake_transport);
+
+ iodev_set_format(iodev, &format);
+ time_now.tv_sec = 0;
+ time_now.tv_nsec = 0;
+ iodev->open_dev(iodev);
+ ASSERT_NE(write_callback, (void *)NULL);
+
+ ASSERT_EQ(iodev->min_buffer_level, 400);
+
+ frames = 700;
+ iodev->get_buffer(iodev, &area, &frames);
+ ASSERT_EQ(700, frames);
+ ASSERT_EQ(700, area->frames);
+
+ /* Fake 111 frames in pre-fill*/
+ a2dp_encode_processed_bytes_val[0] = 111;
+ a2dp_write_return_val[0] = -EAGAIN;
+
+ /* First call to a2dp_encode() processed 800 bytes. */
+ a2dp_encode_processed_bytes_val[1] = 800;
+ a2dp_encode_processed_bytes_val[2] = 0;
+ a2dp_write_return_val[1] = 200;
+
+ /* put_buffer shouldn't trigger the 2nd call to a2dp_encode() because
+ * buffer is low. Fake some data to make sure this test case will fail
+ * when a2dp_encode() called twice.
+ */
+ a2dp_encode_processed_bytes_val[3] = 800;
+ a2dp_encode_processed_bytes_val[4] = 0;
+ a2dp_write_return_val[2] = -EAGAIN;
+
+ time_now.tv_nsec = 10000000;
+ iodev->put_buffer(iodev, 700);
+
+ time_now.tv_nsec = 20000000;
+ EXPECT_EQ(500, iodev->frames_queued(iodev, &tstamp));
+ EXPECT_EQ(tstamp.tv_sec, time_now.tv_sec);
+ EXPECT_EQ(tstamp.tv_nsec, time_now.tv_nsec);
}
} // namespace
@@ -320,7 +375,8 @@ int cras_bt_transport_acquire(struct cras_bt_transport *transport)
return 0;
}
-int cras_bt_transport_release(struct cras_bt_transport *transport)
+int cras_bt_transport_release(struct cras_bt_transport *transport,
+ unsigned int blocking)
{
cras_bt_transport_release_called++;
return 0;
@@ -342,6 +398,11 @@ uint16_t cras_bt_transport_write_mtu(const struct cras_bt_transport *transport)
return cras_bt_transport_write_mtu_ret;
}
+int cras_bt_transport_set_volume(struct cras_bt_transport *transport,
+ uint16_t volume)
+{
+ return 0;
+}
void cras_iodev_free_format(struct cras_iodev *iodev)
{
@@ -409,6 +470,22 @@ void cras_bt_device_rm_iodev(struct cras_bt_device *device,
cras_bt_device_rm_iodev_called++;
}
+int cras_bt_device_get_use_hardware_volume(struct cras_bt_device *device)
+{
+ return 0;
+}
+
+int cras_bt_device_cancel_suspend(struct cras_bt_device *device)
+{
+ return 0;
+}
+
+int cras_bt_device_schedule_suspend(struct cras_bt_device *device,
+ unsigned int msec)
+{
+ return 0;
+}
+
int init_a2dp(struct a2dp_info *a2dp, a2dp_sbc_t *sbc)
{
init_a2dp_called++;
@@ -479,6 +556,10 @@ void cras_audio_area_config_buf_pointers(struct cras_audio_area *area,
dummy_audio_area->channels[0].buf = base_buffer;
}
+struct audio_thread *cras_iodev_list_get_audio_thread()
+{
+ return NULL;
+}
// From audio_thread
struct audio_thread_event_log *atlog;
@@ -487,7 +568,8 @@ void audio_thread_add_write_callback(int fd, thread_callback cb, void *data) {
write_callback_data = data;
}
-void audio_thread_rm_callback(int fd) {
+int audio_thread_rm_callback_sync(struct audio_thread *thread, int fd) {
+ return 0;
}
void audio_thread_enable_callback(int fd, int enabled) {
diff --git a/cras/src/tests/alert_unittest.cc b/cras/src/tests/alert_unittest.cc
index e1073cfa..f013c1bd 100644
--- a/cras/src/tests/alert_unittest.cc
+++ b/cras/src/tests/alert_unittest.cc
@@ -11,11 +11,12 @@
namespace {
-void callback1(void *arg);
-void callback2(void *arg);
+void callback1(void *arg, void *data);
+void callback2(void *arg, void *data);
void prepare(struct cras_alert *alert);
static int cb1_called = 0;
+static void *cb1_data;
static int cb2_called = 0;
static int cb2_set_pending = 0;
static int prepare_called = 0;
@@ -28,7 +29,7 @@ void ResetStub() {
}
TEST(Alert, OneCallback) {
- struct cras_alert *alert = cras_alert_create(NULL);
+ struct cras_alert *alert = cras_alert_create(NULL, 0);
cras_alert_add_callback(alert, &callback1, NULL);
ResetStub();
cras_alert_pending(alert);
@@ -38,8 +39,67 @@ TEST(Alert, OneCallback) {
cras_alert_destroy(alert);
}
+TEST(Alert, OneCallbackPost2Call1) {
+ struct cras_alert *alert = cras_alert_create(NULL, 0);
+ cras_alert_add_callback(alert, &callback1, NULL);
+ ResetStub();
+ // Alert twice, callback should only be called once.
+ cras_alert_pending(alert);
+ cras_alert_pending(alert);
+ EXPECT_EQ(0, cb1_called);
+ cras_alert_process_all_pending_alerts();
+ EXPECT_EQ(1, cb1_called);
+ cras_alert_destroy(alert);
+}
+
+TEST(Alert, OneCallbackWithData) {
+ struct cras_alert *alert = cras_alert_create(NULL, 0);
+ const char *data = "ThisIsMyData";
+ cras_alert_add_callback(alert, &callback1, NULL);
+ ResetStub();
+ cras_alert_pending_data(alert, (void *)data, strlen(data) + 1);
+ EXPECT_EQ(0, cb1_called);
+ cras_alert_process_all_pending_alerts();
+ EXPECT_EQ(1, cb1_called);
+ EXPECT_EQ(0, strcmp(data, (const char *)cb1_data));
+ cras_alert_destroy(alert);
+}
+
+TEST(Alert, OneCallbackTwoDataCalledOnce) {
+ struct cras_alert *alert = cras_alert_create(NULL, 0);
+ const char *data = "ThisIsMyData";
+ const char *data2 = "ThisIsMyData2";
+ cras_alert_add_callback(alert, &callback1, NULL);
+ ResetStub();
+ // Callback called with last data only.
+ cras_alert_pending_data(alert, (void *)data, strlen(data) + 1);
+ cras_alert_pending_data(alert, (void *)data2, strlen(data2) + 1);
+ EXPECT_EQ(0, cb1_called);
+ cras_alert_process_all_pending_alerts();
+ EXPECT_EQ(1, cb1_called);
+ EXPECT_EQ(0, strcmp(data2, (const char *)cb1_data));
+ cras_alert_destroy(alert);
+}
+
+TEST(Alert, OneCallbackTwoDataKeepAll) {
+ struct cras_alert *alert = cras_alert_create(
+ NULL, CRAS_ALERT_FLAG_KEEP_ALL_DATA);
+ const char *data = "ThisIsMyData";
+ const char *data2 = "ThisIsMyData2";
+ cras_alert_add_callback(alert, &callback1, NULL);
+ ResetStub();
+ // Callbacks with data should each be called.
+ cras_alert_pending_data(alert, (void *)data, strlen(data) + 1);
+ cras_alert_pending_data(alert, (void *)data2, strlen(data2) + 1);
+ EXPECT_EQ(0, cb1_called);
+ cras_alert_process_all_pending_alerts();
+ EXPECT_EQ(2, cb1_called);
+ EXPECT_EQ(0, strcmp(data2, (const char *)cb1_data));
+ cras_alert_destroy(alert);
+}
+
TEST(Alert, TwoCallbacks) {
- struct cras_alert *alert = cras_alert_create(NULL);
+ struct cras_alert *alert = cras_alert_create(NULL, 0);
cras_alert_add_callback(alert, &callback1, NULL);
cras_alert_add_callback(alert, &callback2, NULL);
ResetStub();
@@ -53,7 +113,7 @@ TEST(Alert, TwoCallbacks) {
}
TEST(Alert, NoPending) {
- struct cras_alert *alert = cras_alert_create(NULL);
+ struct cras_alert *alert = cras_alert_create(NULL, 0);
cras_alert_add_callback(alert, &callback1, NULL);
ResetStub();
EXPECT_EQ(0, cb1_called);
@@ -63,8 +123,8 @@ TEST(Alert, NoPending) {
}
TEST(Alert, PendingInCallback) {
- struct cras_alert *alert1 = cras_alert_create(NULL);
- struct cras_alert *alert2 = cras_alert_create(NULL);
+ struct cras_alert *alert1 = cras_alert_create(NULL, 0);
+ struct cras_alert *alert2 = cras_alert_create(NULL, 0);
cras_alert_add_callback(alert1, &callback1, NULL);
cras_alert_add_callback(alert2, &callback2, alert1);
ResetStub();
@@ -80,7 +140,7 @@ TEST(Alert, PendingInCallback) {
}
TEST(Alert, Prepare) {
- struct cras_alert *alert = cras_alert_create(prepare);
+ struct cras_alert *alert = cras_alert_create(prepare, 0);
cras_alert_add_callback(alert, &callback1, NULL);
ResetStub();
cras_alert_pending(alert);
@@ -92,8 +152,8 @@ TEST(Alert, Prepare) {
}
TEST(Alert, TwoAlerts) {
- struct cras_alert *alert1 = cras_alert_create(prepare);
- struct cras_alert *alert2 = cras_alert_create(prepare);
+ struct cras_alert *alert1 = cras_alert_create(prepare, 0);
+ struct cras_alert *alert2 = cras_alert_create(prepare, 0);
cras_alert_add_callback(alert1, &callback1, NULL);
cras_alert_add_callback(alert2, &callback2, NULL);
@@ -128,12 +188,13 @@ TEST(Alert, TwoAlerts) {
cras_alert_destroy_all();
}
-void callback1(void *arg)
+void callback1(void *arg, void *data)
{
cb1_called++;
+ cb1_data = data;
}
-void callback2(void *arg)
+void callback2(void *arg, void *data)
{
cb2_called++;
if (cb2_set_pending) {
diff --git a/cras/src/tests/alsa_card_unittest.cc b/cras/src/tests/alsa_card_unittest.cc
index a5c4fec8..741c6b4f 100644
--- a/cras/src/tests/alsa_card_unittest.cc
+++ b/cras/src/tests/alsa_card_unittest.cc
@@ -4,14 +4,19 @@
#include <gtest/gtest.h>
#include <iniparser.h>
+#include <map>
#include <stdio.h>
+#include <syslog.h>
+#include <sys/param.h>
extern "C" {
#include "cras_alsa_card.h"
#include "cras_alsa_io.h"
#include "cras_alsa_mixer.h"
+#include "cras_alsa_ucm.h"
#include "cras_types.h"
#include "cras_util.h"
+#include "utlist.h"
}
namespace {
@@ -20,9 +25,22 @@ static size_t cras_alsa_mixer_create_called;
static struct cras_alsa_mixer *cras_alsa_mixer_create_return;
static size_t cras_alsa_mixer_destroy_called;
static size_t cras_alsa_iodev_create_called;
-static struct cras_iodev *cras_alsa_iodev_create_return;
+static struct cras_iodev **cras_alsa_iodev_create_return;
+static struct cras_iodev *cras_alsa_iodev_create_default_return[] = {
+ reinterpret_cast<struct cras_iodev *>(2),
+ reinterpret_cast<struct cras_iodev *>(3),
+ reinterpret_cast<struct cras_iodev *>(4),
+ reinterpret_cast<struct cras_iodev *>(5),
+};
+static size_t cras_alsa_iodev_create_return_size;
+static size_t cras_alsa_iodev_legacy_complete_init_called;
+static size_t cras_alsa_iodev_ucm_add_nodes_and_jacks_called;
+static size_t cras_alsa_iodev_ucm_complete_init_called;
static size_t cras_alsa_iodev_destroy_called;
static struct cras_iodev *cras_alsa_iodev_destroy_arg;
+static size_t cras_alsa_iodev_index_called;
+static std::map<struct cras_iodev *, unsigned int> cras_alsa_iodev_index_return;
+static int alsa_iodev_has_hctl_jacks_return;
static size_t snd_ctl_open_called;
static size_t snd_ctl_open_return;
static size_t snd_ctl_close_called;
@@ -38,6 +56,21 @@ static size_t snd_ctl_pcm_info_rets_size;
static size_t snd_ctl_pcm_info_rets_index;
static size_t snd_ctl_card_info_called;
static int snd_ctl_card_info_ret;
+static size_t snd_hctl_open_called;
+static int snd_hctl_open_return_value;
+static int snd_hctl_close_called;
+static size_t snd_hctl_nonblock_called;
+static snd_hctl_t *snd_hctl_open_pointer_val;
+static size_t snd_hctl_load_called;
+static int snd_hctl_load_return_value;
+static struct pollfd *snd_hctl_poll_descriptors_fds;
+static size_t snd_hctl_poll_descriptors_num_fds;
+static size_t snd_hctl_poll_descriptors_called;
+static size_t cras_system_add_select_fd_called;
+static std::vector<int> cras_system_add_select_fd_values;
+static size_t cras_system_rm_select_fd_called;
+static std::vector<int> cras_system_rm_select_fd_values;
+static size_t snd_hctl_handle_events_called;
static size_t iniparser_freedict_called;
static size_t iniparser_load_called;
static struct cras_device_blacklist *fake_blacklist;
@@ -49,14 +82,30 @@ static size_t ucm_get_flag_called;
static char ucm_get_flag_name[64];
static char* device_config_dir;
static const char* cras_card_config_dir;
+static struct mixer_name *ucm_get_coupled_mixer_names_return_value;
+static struct mixer_name *coupled_output_names_value;
+static int ucm_has_fully_specified_ucm_flag_return_value;
+static int ucm_get_sections_called;
+static struct ucm_section *ucm_get_sections_return_value;
+static size_t cras_alsa_mixer_add_controls_in_section_called;
+static int cras_alsa_mixer_add_controls_in_section_return_value;
static void ResetStubData() {
cras_alsa_mixer_create_called = 0;
cras_alsa_mixer_create_return = reinterpret_cast<struct cras_alsa_mixer *>(1);
cras_alsa_mixer_destroy_called = 0;
+ cras_alsa_iodev_destroy_arg = NULL;
cras_alsa_iodev_create_called = 0;
- cras_alsa_iodev_create_return = reinterpret_cast<struct cras_iodev *>(2);
+ cras_alsa_iodev_create_return = cras_alsa_iodev_create_default_return;
+ cras_alsa_iodev_create_return_size =
+ ARRAY_SIZE(cras_alsa_iodev_create_default_return);
+ cras_alsa_iodev_legacy_complete_init_called = 0;
+ cras_alsa_iodev_ucm_add_nodes_and_jacks_called = 0;
+ cras_alsa_iodev_ucm_complete_init_called = 0;
cras_alsa_iodev_destroy_called = 0;
+ cras_alsa_iodev_index_called = 0;
+ cras_alsa_iodev_index_return.clear();
+ alsa_iodev_has_hctl_jacks_return = 1;
snd_ctl_open_called = 0;
snd_ctl_open_return = 0;
snd_ctl_close_called = 0;
@@ -70,6 +119,22 @@ static void ResetStubData() {
snd_ctl_pcm_info_rets_index = 0;
snd_ctl_card_info_called = 0;
snd_ctl_card_info_ret = 0;
+ snd_hctl_open_called = 0;
+ snd_hctl_open_return_value = 0;
+ snd_hctl_open_pointer_val = reinterpret_cast<snd_hctl_t *>(0x4323);
+ snd_hctl_load_called = 0;
+ snd_hctl_load_return_value = 0;
+ snd_hctl_close_called = 0;
+ snd_hctl_nonblock_called = 0;
+ snd_hctl_poll_descriptors_num_fds = 0;
+ snd_hctl_poll_descriptors_called = 0;
+ snd_hctl_handle_events_called = 0;
+ snd_hctl_poll_descriptors_num_fds = 0;
+ snd_hctl_poll_descriptors_called = 0;
+ cras_system_add_select_fd_called = 0;
+ cras_system_add_select_fd_values.clear();
+ cras_system_rm_select_fd_called = 0;
+ cras_system_rm_select_fd_values.clear();
iniparser_freedict_called = 0;
iniparser_load_called = 0;
fake_blacklist = reinterpret_cast<struct cras_device_blacklist *>(3);
@@ -81,6 +146,14 @@ static void ResetStubData() {
memset(ucm_get_flag_name, 0, sizeof(ucm_get_flag_name));
device_config_dir = reinterpret_cast<char *>(3);
cras_card_config_dir = NULL;
+ ucm_get_coupled_mixer_names_return_value = NULL;
+ mixer_name_free(coupled_output_names_value);
+ coupled_output_names_value = NULL;
+ ucm_has_fully_specified_ucm_flag_return_value = 0;
+ ucm_get_sections_called = 0;
+ ucm_get_sections_return_value = NULL;
+ cras_alsa_mixer_add_controls_in_section_called = 0;
+ cras_alsa_mixer_add_controls_in_section_return_value = 0;
}
TEST(AlsaCard, CreateFailInvalidCard) {
@@ -90,7 +163,8 @@ TEST(AlsaCard, CreateFailInvalidCard) {
ResetStubData();
card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
card_info.card_index = 55;
- c = cras_alsa_card_create(&card_info, device_config_dir, fake_blacklist);
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
EXPECT_EQ(static_cast<struct cras_alsa_card *>(NULL), c);
EXPECT_EQ(snd_ctl_close_called, snd_ctl_open_called);
EXPECT_EQ(cras_alsa_mixer_create_called, cras_alsa_mixer_destroy_called);
@@ -104,7 +178,8 @@ TEST(AlsaCard, CreateFailMixerInit) {
card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
card_info.card_index = 0;
cras_alsa_mixer_create_return = static_cast<struct cras_alsa_mixer *>(NULL);
- c = cras_alsa_card_create(&card_info, device_config_dir, fake_blacklist);
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
EXPECT_EQ(static_cast<struct cras_alsa_card *>(NULL), c);
EXPECT_EQ(snd_ctl_close_called, snd_ctl_open_called);
EXPECT_EQ(1, cras_alsa_mixer_create_called);
@@ -119,12 +194,129 @@ TEST(AlsaCard, CreateFailCtlOpen) {
card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
card_info.card_index = 0;
snd_ctl_open_return = -1;
- c = cras_alsa_card_create(&card_info, device_config_dir, fake_blacklist);
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
EXPECT_EQ(static_cast<struct cras_alsa_card *>(NULL), c);
EXPECT_EQ(1, snd_ctl_open_called);
EXPECT_EQ(0, snd_ctl_close_called);
- EXPECT_EQ(cras_alsa_mixer_create_called, cras_alsa_mixer_destroy_called);
EXPECT_EQ(iniparser_load_called, iniparser_freedict_called);
+ EXPECT_EQ(0, cras_alsa_mixer_create_called);
+}
+
+TEST(AlsaCard, CreateFailHctlOpen) {
+ struct cras_alsa_card *c;
+ cras_alsa_card_info card_info;
+
+ ResetStubData();
+ card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
+ card_info.card_index = 0;
+ snd_hctl_open_pointer_val = NULL;
+ snd_hctl_open_return_value = -1;
+
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
+ EXPECT_NE(static_cast<struct cras_alsa_card *>(NULL), c);
+ EXPECT_EQ(1, snd_ctl_open_called);
+ EXPECT_EQ(1, snd_ctl_close_called);
+ EXPECT_EQ(iniparser_load_called, iniparser_freedict_called);
+ EXPECT_EQ(1, snd_hctl_open_called);
+ EXPECT_EQ(0, snd_hctl_nonblock_called);
+ EXPECT_EQ(0, snd_hctl_load_called);
+ EXPECT_EQ(1, cras_alsa_mixer_create_called);
+}
+
+TEST(AlsaCard, CreateFailHctlLoad) {
+ struct cras_alsa_card *c;
+ cras_alsa_card_info card_info;
+
+ ResetStubData();
+ card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
+ card_info.card_index = 0;
+ snd_hctl_load_return_value = -1;
+
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
+ EXPECT_EQ(static_cast<struct cras_alsa_card *>(NULL), c);
+ EXPECT_EQ(1, snd_ctl_open_called);
+ EXPECT_EQ(1, snd_ctl_close_called);
+ EXPECT_EQ(iniparser_load_called, iniparser_freedict_called);
+ EXPECT_EQ(1, snd_hctl_open_called);
+ EXPECT_EQ(1, snd_hctl_nonblock_called);
+ EXPECT_EQ(1, snd_hctl_load_called);
+ EXPECT_EQ(0, cras_alsa_mixer_create_called);
+}
+
+TEST(AlsaCard, AddSelectForHctlNoDevices) {
+ struct pollfd poll_fds[] = {
+ {3, 0, 0},
+ };
+
+ struct cras_alsa_card *c;
+ cras_alsa_card_info card_info;
+
+ ResetStubData();
+ card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
+ card_info.card_index = 0;
+ snd_hctl_poll_descriptors_fds = poll_fds;
+ snd_hctl_poll_descriptors_num_fds = ARRAY_SIZE(poll_fds);
+
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
+ EXPECT_NE(static_cast<struct cras_alsa_card *>(NULL), c);
+ EXPECT_EQ(1, snd_ctl_open_called);
+ EXPECT_EQ(1, snd_ctl_close_called);
+ EXPECT_EQ(iniparser_load_called, iniparser_freedict_called);
+ EXPECT_EQ(1, snd_hctl_open_called);
+ EXPECT_EQ(1, snd_hctl_nonblock_called);
+ EXPECT_EQ(1, snd_hctl_load_called);
+ EXPECT_EQ(1, cras_alsa_mixer_create_called);
+ EXPECT_EQ(0, cras_system_add_select_fd_called);
+ cras_alsa_card_destroy(c);
+ EXPECT_EQ(0, cras_system_rm_select_fd_called);
+}
+
+TEST(AlsaCard, AddSelectForHctlWithDevices) {
+ struct pollfd poll_fds[] = {
+ {3, 0, 0},
+ };
+ int dev_nums[] = {0};
+ int info_rets[] = {0, -1};
+
+ struct cras_alsa_card *c;
+ cras_alsa_card_info card_info;
+
+ ResetStubData();
+ snd_ctl_pcm_next_device_set_devs_size = ARRAY_SIZE(dev_nums);
+ snd_ctl_pcm_next_device_set_devs = dev_nums;
+ snd_ctl_pcm_info_rets_size = ARRAY_SIZE(info_rets);
+ snd_ctl_pcm_info_rets = info_rets;
+ card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
+ card_info.card_index = 0;
+ snd_hctl_poll_descriptors_fds = poll_fds;
+ snd_hctl_poll_descriptors_num_fds = ARRAY_SIZE(poll_fds);
+
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
+ EXPECT_NE(static_cast<struct cras_alsa_card *>(NULL), c);
+ EXPECT_EQ(snd_ctl_close_called, snd_ctl_open_called);
+ EXPECT_EQ(2, snd_ctl_pcm_next_device_called);
+ EXPECT_EQ(1, cras_alsa_iodev_create_called);
+ EXPECT_EQ(0, cras_alsa_iodev_index_called);
+ EXPECT_EQ(1, snd_ctl_card_info_called);
+ EXPECT_EQ(1, ucm_create_called);
+ EXPECT_EQ(1, ucm_get_dev_for_mixer_called);
+ EXPECT_EQ(1, ucm_get_flag_called);
+ EXPECT_EQ(0, strcmp(ucm_get_flag_name, "ExtraMainVolume"));
+ EXPECT_EQ(cras_card_config_dir, device_config_dir);
+ EXPECT_EQ(iniparser_load_called, iniparser_freedict_called);
+ EXPECT_EQ(1, snd_hctl_open_called);
+ EXPECT_EQ(1, snd_hctl_nonblock_called);
+ EXPECT_EQ(1, snd_hctl_load_called);
+ EXPECT_EQ(1, cras_alsa_mixer_create_called);
+ ASSERT_EQ(1, cras_system_add_select_fd_called);
+ EXPECT_EQ(3, cras_system_add_select_fd_values[0]);
+ cras_alsa_card_destroy(c);
+ EXPECT_EQ(ARRAY_SIZE(poll_fds), cras_system_rm_select_fd_called);
}
TEST(AlsaCard, CreateFailCtlCardInfo) {
@@ -135,7 +327,8 @@ TEST(AlsaCard, CreateFailCtlCardInfo) {
card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
card_info.card_index = 0;
snd_ctl_card_info_ret = -1;
- c = cras_alsa_card_create(&card_info, device_config_dir, fake_blacklist);
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
EXPECT_EQ(static_cast<struct cras_alsa_card *>(NULL), c);
EXPECT_EQ(1, snd_ctl_open_called);
EXPECT_EQ(1, snd_ctl_close_called);
@@ -150,12 +343,16 @@ TEST(AlsaCard, CreateNoDevices) {
ResetStubData();
card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
card_info.card_index = 1;
- c = cras_alsa_card_create(&card_info, device_config_dir, fake_blacklist);
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
EXPECT_NE(static_cast<struct cras_alsa_card *>(NULL), c);
EXPECT_EQ(snd_ctl_close_called, snd_ctl_open_called);
EXPECT_EQ(1, snd_ctl_pcm_next_device_called);
EXPECT_EQ(0, cras_alsa_iodev_create_called);
+ EXPECT_EQ(0, cras_alsa_iodev_legacy_complete_init_called);
EXPECT_EQ(1, cras_alsa_card_get_index(c));
+ EXPECT_EQ(0, ucm_get_sections_called);
+ EXPECT_EQ(0, cras_alsa_mixer_add_controls_in_section_called);
cras_alsa_card_destroy(c);
EXPECT_EQ(0, cras_alsa_iodev_destroy_called);
@@ -171,7 +368,8 @@ TEST(AlsaCard, CreateOneOutputNextDevError) {
snd_ctl_pcm_next_device_return_error = true;
card_info.card_type = ALSA_CARD_TYPE_USB;
card_info.card_index = 0;
- c = cras_alsa_card_create(&card_info, device_config_dir, fake_blacklist);
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
EXPECT_EQ(static_cast<struct cras_alsa_card *>(NULL), c);
EXPECT_EQ(cras_alsa_mixer_create_called, cras_alsa_mixer_destroy_called);
EXPECT_EQ(snd_ctl_open_called, snd_ctl_close_called);
@@ -191,22 +389,27 @@ TEST(AlsaCard, CreateOneOutput) {
snd_ctl_pcm_info_rets = info_rets;
card_info.card_type = ALSA_CARD_TYPE_USB;
card_info.card_index = 0;
- c = cras_alsa_card_create(&card_info, device_config_dir, fake_blacklist);
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
EXPECT_NE(static_cast<struct cras_alsa_card *>(NULL), c);
EXPECT_EQ(snd_ctl_close_called, snd_ctl_open_called);
EXPECT_EQ(2, snd_ctl_pcm_next_device_called);
EXPECT_EQ(1, cras_alsa_iodev_create_called);
+ EXPECT_EQ(1, cras_alsa_iodev_legacy_complete_init_called);
+ EXPECT_EQ(0, cras_alsa_iodev_index_called);
EXPECT_EQ(1, snd_ctl_card_info_called);
EXPECT_EQ(1, ucm_create_called);
EXPECT_EQ(1, ucm_get_dev_for_mixer_called);
EXPECT_EQ(1, ucm_get_flag_called);
EXPECT_EQ(0, strcmp(ucm_get_flag_name, "ExtraMainVolume"));
EXPECT_EQ(cras_card_config_dir, device_config_dir);
+ EXPECT_EQ(0, ucm_get_sections_called);
+ EXPECT_EQ(0, cras_alsa_mixer_add_controls_in_section_called);
cras_alsa_card_destroy(c);
EXPECT_EQ(1, ucm_destroy_called);
EXPECT_EQ(1, cras_alsa_iodev_destroy_called);
- EXPECT_EQ(cras_alsa_iodev_create_return, cras_alsa_iodev_destroy_arg);
+ EXPECT_EQ(cras_alsa_iodev_create_return[0], cras_alsa_iodev_destroy_arg);
EXPECT_EQ(cras_alsa_mixer_create_called, cras_alsa_mixer_destroy_called);
EXPECT_EQ(iniparser_load_called, iniparser_freedict_called);
}
@@ -222,20 +425,23 @@ TEST(AlsaCard, CreateOneOutputBlacklisted) {
snd_ctl_pcm_next_device_set_devs = dev_nums;
snd_ctl_pcm_info_rets_size = ARRAY_SIZE(info_rets);
snd_ctl_pcm_info_rets = info_rets;
+ alsa_iodev_has_hctl_jacks_return = 0;
cras_device_blacklist_check_retval = 1;
card_info.card_type = ALSA_CARD_TYPE_USB;
card_info.card_index = 0;
- c = cras_alsa_card_create(&card_info, device_config_dir, fake_blacklist);
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
EXPECT_NE(static_cast<struct cras_alsa_card *>(NULL), c);
EXPECT_EQ(snd_ctl_close_called, snd_ctl_open_called);
EXPECT_EQ(2, snd_ctl_pcm_next_device_called);
EXPECT_EQ(1, snd_ctl_card_info_called);
EXPECT_EQ(0, cras_alsa_iodev_create_called);
+ EXPECT_EQ(0, cras_alsa_iodev_legacy_complete_init_called);
EXPECT_EQ(cras_card_config_dir, device_config_dir);
cras_alsa_card_destroy(c);
EXPECT_EQ(0, cras_alsa_iodev_destroy_called);
- EXPECT_EQ(cras_alsa_iodev_create_return, cras_alsa_iodev_destroy_arg);
+ EXPECT_EQ(NULL, cras_alsa_iodev_destroy_arg);
EXPECT_EQ(cras_alsa_mixer_create_called, cras_alsa_mixer_destroy_called);
EXPECT_EQ(iniparser_load_called, iniparser_freedict_called);
}
@@ -253,17 +459,55 @@ TEST(AlsaCard, CreateTwoOutputs) {
snd_ctl_pcm_info_rets = info_rets;
card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
card_info.card_index = 0;
- c = cras_alsa_card_create(&card_info, device_config_dir, fake_blacklist);
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
EXPECT_NE(static_cast<struct cras_alsa_card *>(NULL), c);
EXPECT_EQ(snd_ctl_close_called, snd_ctl_open_called);
EXPECT_EQ(3, snd_ctl_pcm_next_device_called);
EXPECT_EQ(2, cras_alsa_iodev_create_called);
+ EXPECT_EQ(2, cras_alsa_iodev_legacy_complete_init_called);
+ EXPECT_EQ(1, cras_alsa_iodev_index_called);
EXPECT_EQ(1, snd_ctl_card_info_called);
EXPECT_EQ(cras_card_config_dir, device_config_dir);
+ EXPECT_EQ(0, ucm_get_sections_called);
+ EXPECT_EQ(0, cras_alsa_mixer_add_controls_in_section_called);
cras_alsa_card_destroy(c);
EXPECT_EQ(2, cras_alsa_iodev_destroy_called);
- EXPECT_EQ(cras_alsa_iodev_create_return, cras_alsa_iodev_destroy_arg);
+ EXPECT_EQ(cras_alsa_iodev_create_return[1], cras_alsa_iodev_destroy_arg);
+ EXPECT_EQ(cras_alsa_mixer_create_called, cras_alsa_mixer_destroy_called);
+ EXPECT_EQ(iniparser_load_called, iniparser_freedict_called);
+}
+
+TEST(AlsaCard, CreateTwoDuplicateDeviceIndex) {
+ struct cras_alsa_card *c;
+ int dev_nums[] = {0, 0};
+ int info_rets[] = {0, -1, 0};
+ cras_alsa_card_info card_info;
+
+ ResetStubData();
+ snd_ctl_pcm_next_device_set_devs_size = ARRAY_SIZE(dev_nums);
+ snd_ctl_pcm_next_device_set_devs = dev_nums;
+ snd_ctl_pcm_info_rets_size = ARRAY_SIZE(info_rets);
+ snd_ctl_pcm_info_rets = info_rets;
+ card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
+ card_info.card_index = 0;
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
+ EXPECT_NE(static_cast<struct cras_alsa_card *>(NULL), c);
+ EXPECT_EQ(snd_ctl_close_called, snd_ctl_open_called);
+ EXPECT_EQ(3, snd_ctl_pcm_next_device_called);
+ EXPECT_EQ(1, cras_alsa_iodev_create_called);
+ EXPECT_EQ(2, cras_alsa_iodev_legacy_complete_init_called);
+ EXPECT_EQ(1, cras_alsa_iodev_index_called);
+ EXPECT_EQ(1, snd_ctl_card_info_called);
+ EXPECT_EQ(cras_card_config_dir, device_config_dir);
+ EXPECT_EQ(0, ucm_get_sections_called);
+ EXPECT_EQ(0, cras_alsa_mixer_add_controls_in_section_called);
+
+ cras_alsa_card_destroy(c);
+ EXPECT_EQ(1, cras_alsa_iodev_destroy_called);
+ EXPECT_EQ(cras_alsa_iodev_create_return[0], cras_alsa_iodev_destroy_arg);
EXPECT_EQ(cras_alsa_mixer_create_called, cras_alsa_mixer_destroy_called);
EXPECT_EQ(iniparser_load_called, iniparser_freedict_called);
}
@@ -281,16 +525,21 @@ TEST(AlsaCard, CreateOneInput) {
snd_ctl_pcm_info_rets = info_rets;
card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
card_info.card_index = 0;
- c = cras_alsa_card_create(&card_info, device_config_dir, fake_blacklist);
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
EXPECT_NE(static_cast<struct cras_alsa_card *>(NULL), c);
EXPECT_EQ(snd_ctl_close_called, snd_ctl_open_called);
EXPECT_EQ(2, snd_ctl_pcm_next_device_called);
EXPECT_EQ(1, cras_alsa_iodev_create_called);
+ EXPECT_EQ(1, cras_alsa_iodev_legacy_complete_init_called);
+ EXPECT_EQ(0, cras_alsa_iodev_index_called);
EXPECT_EQ(cras_card_config_dir, device_config_dir);
+ EXPECT_EQ(0, ucm_get_sections_called);
+ EXPECT_EQ(0, cras_alsa_mixer_add_controls_in_section_called);
cras_alsa_card_destroy(c);
EXPECT_EQ(1, cras_alsa_iodev_destroy_called);
- EXPECT_EQ(cras_alsa_iodev_create_return, cras_alsa_iodev_destroy_arg);
+ EXPECT_EQ(cras_alsa_iodev_create_return[0], cras_alsa_iodev_destroy_arg);
EXPECT_EQ(cras_alsa_mixer_create_called, cras_alsa_mixer_destroy_called);
EXPECT_EQ(iniparser_load_called, iniparser_freedict_called);
}
@@ -308,16 +557,21 @@ TEST(AlsaCard, CreateOneInputAndOneOutput) {
snd_ctl_pcm_info_rets = info_rets;
card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
card_info.card_index = 0;
- c = cras_alsa_card_create(&card_info, device_config_dir, fake_blacklist);
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
EXPECT_NE(static_cast<struct cras_alsa_card *>(NULL), c);
EXPECT_EQ(snd_ctl_close_called, snd_ctl_open_called);
EXPECT_EQ(2, snd_ctl_pcm_next_device_called);
EXPECT_EQ(2, cras_alsa_iodev_create_called);
+ EXPECT_EQ(2, cras_alsa_iodev_legacy_complete_init_called);
+ EXPECT_EQ(0, cras_alsa_iodev_index_called);
EXPECT_EQ(cras_card_config_dir, device_config_dir);
+ EXPECT_EQ(0, ucm_get_sections_called);
+ EXPECT_EQ(0, cras_alsa_mixer_add_controls_in_section_called);
cras_alsa_card_destroy(c);
EXPECT_EQ(2, cras_alsa_iodev_destroy_called);
- EXPECT_EQ(cras_alsa_iodev_create_return, cras_alsa_iodev_destroy_arg);
+ EXPECT_EQ(cras_alsa_iodev_create_return[1], cras_alsa_iodev_destroy_arg);
EXPECT_EQ(cras_alsa_mixer_create_called, cras_alsa_mixer_destroy_called);
EXPECT_EQ(iniparser_load_called, iniparser_freedict_called);
}
@@ -335,30 +589,253 @@ TEST(AlsaCard, CreateOneInputAndOneOutputTwoDevices) {
snd_ctl_pcm_info_rets = info_rets;
card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
card_info.card_index = 0;
- c = cras_alsa_card_create(&card_info, device_config_dir, fake_blacklist);
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
EXPECT_NE(static_cast<struct cras_alsa_card *>(NULL), c);
EXPECT_EQ(snd_ctl_close_called, snd_ctl_open_called);
EXPECT_EQ(3, snd_ctl_pcm_next_device_called);
EXPECT_EQ(2, cras_alsa_iodev_create_called);
+ EXPECT_EQ(2, cras_alsa_iodev_legacy_complete_init_called);
+ EXPECT_EQ(0, cras_alsa_iodev_index_called);
EXPECT_EQ(cras_card_config_dir, device_config_dir);
+ EXPECT_EQ(0, ucm_get_sections_called);
+ EXPECT_EQ(0, cras_alsa_mixer_add_controls_in_section_called);
cras_alsa_card_destroy(c);
EXPECT_EQ(2, cras_alsa_iodev_destroy_called);
- EXPECT_EQ(cras_alsa_iodev_create_return, cras_alsa_iodev_destroy_arg);
+ EXPECT_EQ(cras_alsa_iodev_create_return[1], cras_alsa_iodev_destroy_arg);
EXPECT_EQ(cras_alsa_mixer_create_called, cras_alsa_mixer_destroy_called);
EXPECT_EQ(iniparser_load_called, iniparser_freedict_called);
}
+TEST(AlsaCard, CreateOneOutputWithCoupledMixers) {
+ struct cras_alsa_card *c;
+ int dev_nums[] = {0};
+ int info_rets[] = {0, -1};
+ struct mixer_name *mixer_name_1, *mixer_name_2;
+ /* Use strdup because cras_alsa_card_create will delete it. */
+ const char *name1 = strdup("MixerName1"), *name2 = strdup("MixerName2");
+
+ cras_alsa_card_info card_info;
+
+ ResetStubData();
+ snd_ctl_pcm_next_device_set_devs_size = ARRAY_SIZE(dev_nums);
+ snd_ctl_pcm_next_device_set_devs = dev_nums;
+ snd_ctl_pcm_info_rets_size = ARRAY_SIZE(info_rets);
+ snd_ctl_pcm_info_rets = info_rets;
+ card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
+ card_info.card_index = 0;
+
+ /* Creates a list of mixer names as return value of
+ * ucm_get_coupled_mixer_names. */
+ mixer_name_1 = (struct mixer_name*)malloc(sizeof(*mixer_name_1));
+ mixer_name_2 = (struct mixer_name*)malloc(sizeof(*mixer_name_2));
+ mixer_name_1->name = name1;
+ mixer_name_2->name = name2;
+ DL_APPEND(ucm_get_coupled_mixer_names_return_value, mixer_name_1);
+ DL_APPEND(ucm_get_coupled_mixer_names_return_value, mixer_name_2);
+
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
+
+ EXPECT_NE(static_cast<struct cras_alsa_card *>(NULL), c);
+ EXPECT_EQ(snd_ctl_close_called, snd_ctl_open_called);
+ EXPECT_EQ(2, snd_ctl_pcm_next_device_called);
+ EXPECT_EQ(1, cras_alsa_iodev_create_called);
+ EXPECT_EQ(1, cras_alsa_iodev_legacy_complete_init_called);
+ EXPECT_EQ(0, cras_alsa_iodev_index_called);
+ EXPECT_EQ(1, snd_ctl_card_info_called);
+ EXPECT_EQ(1, ucm_create_called);
+ EXPECT_EQ(1, ucm_get_dev_for_mixer_called);
+ EXPECT_EQ(1, ucm_get_flag_called);
+ EXPECT_EQ(0, strcmp(ucm_get_flag_name, "ExtraMainVolume"));
+ EXPECT_EQ(cras_card_config_dir, device_config_dir);
+ EXPECT_EQ(0, ucm_get_sections_called);
+ EXPECT_EQ(0, cras_alsa_mixer_add_controls_in_section_called);
+
+ /* Checks cras_alsa_card_create can handle the list and pass the names to
+ * cras_alsa_mixer_create. */
+ struct mixer_name *m_name = coupled_output_names_value;
+ EXPECT_EQ(0, m_name ? strcmp(m_name->name, "MixerName1") : 1);
+ if (m_name)
+ m_name = m_name->next;
+ EXPECT_EQ(0, m_name ? strcmp(m_name->name, "MixerName2") : 1);
+
+ cras_alsa_card_destroy(c);
+ EXPECT_EQ(1, ucm_destroy_called);
+ EXPECT_EQ(1, cras_alsa_iodev_destroy_called);
+ EXPECT_EQ(cras_alsa_iodev_create_return[0], cras_alsa_iodev_destroy_arg);
+ EXPECT_EQ(cras_alsa_mixer_create_called, cras_alsa_mixer_destroy_called);
+ EXPECT_EQ(iniparser_load_called, iniparser_freedict_called);
+
+ mixer_name_free(coupled_output_names_value);
+ coupled_output_names_value = NULL;
+}
+
+TEST(AlsaCard, CreateFullyUCMNoSections) {
+ struct cras_alsa_card *c;
+ cras_alsa_card_info card_info;
+
+ ResetStubData();
+ card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
+ card_info.card_index = 0;
+ ucm_has_fully_specified_ucm_flag_return_value = 1;
+ ucm_get_sections_return_value = NULL;
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
+ EXPECT_EQ(static_cast<struct cras_alsa_card *>(NULL), c);
+ EXPECT_EQ(snd_ctl_close_called, snd_ctl_open_called);
+ EXPECT_EQ(0, cras_alsa_iodev_create_called);
+ EXPECT_EQ(0, cras_alsa_iodev_ucm_complete_init_called);
+ EXPECT_EQ(1, snd_ctl_card_info_called);
+ EXPECT_EQ(1, ucm_get_sections_called);
+ EXPECT_EQ(0, cras_alsa_mixer_add_controls_in_section_called);
+
+ cras_alsa_card_destroy(c);
+ EXPECT_EQ(1, ucm_destroy_called);
+ EXPECT_EQ(0, cras_alsa_iodev_destroy_called);
+ EXPECT_EQ(NULL, cras_alsa_iodev_destroy_arg);
+ EXPECT_EQ(cras_alsa_mixer_create_called, cras_alsa_mixer_destroy_called);
+ EXPECT_EQ(iniparser_load_called, iniparser_freedict_called);
+}
+
+struct ucm_section *GenerateUcmSections (void) {
+ struct ucm_section *sections = NULL;
+ struct ucm_section *section;
+
+ section = ucm_section_create("Headphone", 0, CRAS_STREAM_OUTPUT,
+ "my-sound-card Headset Jack", "gpio");
+ ucm_section_add_coupled(section, "HP-L", MIXER_NAME_VOLUME);
+ ucm_section_add_coupled(section, "HP-R", MIXER_NAME_VOLUME);
+ DL_APPEND(sections, section);
+
+ section = ucm_section_create("Speaker", 0, CRAS_STREAM_OUTPUT,
+ NULL, NULL);
+ ucm_section_add_coupled(section, "SPK-L", MIXER_NAME_VOLUME);
+ ucm_section_add_coupled(section, "SPK-R", MIXER_NAME_VOLUME);
+ DL_APPEND(sections, section);
+
+ section = ucm_section_create("Mic", 0, CRAS_STREAM_INPUT,
+ "my-sound-card Headset Jack", "gpio");
+ ucm_section_set_mixer_name(section, "CAPTURE");
+
+ section = ucm_section_create("Internal Mic", 0, CRAS_STREAM_INPUT,
+ NULL, NULL);
+ ucm_section_add_coupled(section, "INT-MIC-L", MIXER_NAME_VOLUME);
+ ucm_section_add_coupled(section, "INT-MIC-R", MIXER_NAME_VOLUME);
+ DL_APPEND(sections, section);
+
+ section = ucm_section_create("Mic", 1, CRAS_STREAM_INPUT,
+ "my-sound-card Headset Jack", "gpio");
+ ucm_section_add_coupled(section, "MIC-L", MIXER_NAME_VOLUME);
+ ucm_section_add_coupled(section, "MIC-R", MIXER_NAME_VOLUME);
+ DL_APPEND(sections, section);
+
+ section = ucm_section_create("HDMI", 2, CRAS_STREAM_OUTPUT,
+ NULL, NULL);
+ ucm_section_set_mixer_name(section, "HDMI");
+ DL_APPEND(sections, section);
+
+ return sections;
+}
+
+TEST(AlsaCard, CreateFullyUCMFailureOnControls) {
+ struct cras_alsa_card *c;
+ cras_alsa_card_info card_info;
+
+ ResetStubData();
+ card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
+ card_info.card_index = 0;
+ ucm_has_fully_specified_ucm_flag_return_value = 1;
+ ucm_get_sections_return_value = GenerateUcmSections();
+ ASSERT_NE(ucm_get_sections_return_value, (struct ucm_section *)NULL);
+
+ cras_alsa_mixer_add_controls_in_section_return_value = -EINVAL;
+
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
+
+ EXPECT_EQ(static_cast<struct cras_alsa_card *>(NULL), c);
+ EXPECT_EQ(snd_ctl_close_called, snd_ctl_open_called);
+ EXPECT_EQ(1, snd_ctl_card_info_called);
+ EXPECT_EQ(1, ucm_get_sections_called);
+ EXPECT_EQ(1, cras_alsa_mixer_add_controls_in_section_called);
+ EXPECT_EQ(0, cras_alsa_iodev_create_called);
+ EXPECT_EQ(0, cras_alsa_iodev_ucm_complete_init_called);
+
+ cras_alsa_card_destroy(c);
+ EXPECT_EQ(1, ucm_destroy_called);
+ EXPECT_EQ(0, cras_alsa_iodev_destroy_called);
+ EXPECT_EQ(NULL, cras_alsa_iodev_destroy_arg);
+ EXPECT_EQ(cras_alsa_mixer_create_called, cras_alsa_mixer_destroy_called);
+ EXPECT_EQ(iniparser_load_called, iniparser_freedict_called);
+}
+
+TEST(AlsaCard, CreateFullyUCMFourDevicesFiveSections) {
+ struct cras_alsa_card *c;
+ cras_alsa_card_info card_info;
+ int info_rets[] = {0, 0, 0, 0, 0, -1};
+
+ ResetStubData();
+ card_info.card_type = ALSA_CARD_TYPE_INTERNAL;
+ card_info.card_index = 0;
+ snd_ctl_pcm_info_rets_size = ARRAY_SIZE(info_rets);
+ snd_ctl_pcm_info_rets = info_rets;
+ ucm_has_fully_specified_ucm_flag_return_value = 1;
+ ucm_get_sections_return_value = GenerateUcmSections();
+ cras_alsa_iodev_index_return[cras_alsa_iodev_create_return[0]] = 0;
+ cras_alsa_iodev_index_return[cras_alsa_iodev_create_return[1]] = 0;
+ cras_alsa_iodev_index_return[cras_alsa_iodev_create_return[2]] = 1;
+ cras_alsa_iodev_index_return[cras_alsa_iodev_create_return[3]] = 2;
+ ASSERT_NE(ucm_get_sections_return_value, (struct ucm_section *)NULL);
+
+ c = cras_alsa_card_create(&card_info, device_config_dir,
+ fake_blacklist, NULL);
+
+ EXPECT_NE(static_cast<struct cras_alsa_card *>(NULL), c);
+ EXPECT_EQ(snd_ctl_close_called, snd_ctl_open_called);
+ EXPECT_EQ(1, snd_ctl_card_info_called);
+ EXPECT_EQ(1, ucm_get_sections_called);
+ EXPECT_EQ(5, snd_ctl_pcm_info_called);
+ EXPECT_EQ(5, cras_alsa_mixer_add_controls_in_section_called);
+ EXPECT_EQ(4, cras_alsa_iodev_create_called);
+ EXPECT_EQ(5, cras_alsa_iodev_ucm_add_nodes_and_jacks_called);
+ EXPECT_EQ(4, cras_alsa_iodev_ucm_complete_init_called);
+
+ cras_alsa_card_destroy(c);
+ EXPECT_EQ(1, ucm_destroy_called);
+ EXPECT_EQ(4, cras_alsa_iodev_destroy_called);
+ EXPECT_EQ(cras_alsa_iodev_create_return[3], cras_alsa_iodev_destroy_arg);
+ EXPECT_EQ(cras_alsa_mixer_create_called, cras_alsa_mixer_destroy_called);
+ EXPECT_EQ(iniparser_load_called, iniparser_freedict_called);
+}
+
+
/* Stubs */
extern "C" {
-struct cras_alsa_mixer *cras_alsa_mixer_create(
- const char *card_name, const struct cras_card_config *config,
- const char *output_names_extra[], size_t output_names_extra_size,
- const char *extra_master_volume) {
+struct cras_alsa_mixer *cras_alsa_mixer_create(const char *card_name) {
cras_alsa_mixer_create_called++;
return cras_alsa_mixer_create_return;
}
+
+int cras_alsa_mixer_add_controls_by_name_matching(
+ struct cras_alsa_mixer* cmix,
+ struct mixer_name *extra_controls,
+ struct mixer_name *coupled_controls) {
+ /* Duplicate coupled_output_names to verify in the end of unittest
+ * because names will get freed later in cras_alsa_card_create. */
+ struct mixer_name *control;
+ DL_FOREACH(coupled_controls, control) {
+ coupled_output_names_value =
+ mixer_name_add(coupled_output_names_value,
+ control->name,
+ CRAS_STREAM_OUTPUT,
+ control->type);
+ }
+ return 0;
+}
+
void cras_alsa_mixer_destroy(struct cras_alsa_mixer *cras_mixer) {
cras_alsa_mixer_destroy_called++;
}
@@ -371,17 +848,46 @@ struct cras_iodev *alsa_iodev_create(size_t card_index,
enum CRAS_ALSA_CARD_TYPE card_type,
int is_first,
struct cras_alsa_mixer *mixer,
- snd_use_case_mgr_t *ucm,
+ const struct cras_card_config *config,
+ struct cras_use_case_mgr *ucm,
+ snd_hctl_t *hctl,
enum CRAS_STREAM_DIRECTION direction,
- size_t usb_vid,
- size_t usb_pid) {
+ size_t usb_vid,
+ size_t usb_pid,
+ char *usb_serial_number) {
+ struct cras_iodev *result = NULL;
+ if (cras_alsa_iodev_create_called < cras_alsa_iodev_create_return_size)
+ result = cras_alsa_iodev_create_return[cras_alsa_iodev_create_called];
cras_alsa_iodev_create_called++;
- return cras_alsa_iodev_create_return;
+ return result;
}
void alsa_iodev_destroy(struct cras_iodev *iodev) {
cras_alsa_iodev_destroy_called++;
cras_alsa_iodev_destroy_arg = iodev;
}
+int alsa_iodev_legacy_complete_init(struct cras_iodev *iodev) {
+ cras_alsa_iodev_legacy_complete_init_called++;
+ return 0;
+}
+int alsa_iodev_ucm_add_nodes_and_jacks(struct cras_iodev *iodev,
+ struct ucm_section *section) {
+ cras_alsa_iodev_ucm_add_nodes_and_jacks_called++;
+ return 0;
+}
+void alsa_iodev_ucm_complete_init(struct cras_iodev *iodev) {
+ cras_alsa_iodev_ucm_complete_init_called++;
+}
+unsigned alsa_iodev_index(struct cras_iodev *iodev) {
+ std::map<struct cras_iodev *, unsigned int>::iterator i;
+ cras_alsa_iodev_index_called++;
+ i = cras_alsa_iodev_index_return.find(iodev);
+ if (i != cras_alsa_iodev_index_return.end())
+ return i->second;
+ return 0;
+}
+int alsa_iodev_has_hctl_jacks(struct cras_iodev *iodev) {
+ return alsa_iodev_has_hctl_jacks_return;
+}
size_t snd_pcm_info_sizeof() {
return 10;
@@ -444,6 +950,52 @@ const char *snd_ctl_card_info_get_name(const snd_ctl_card_info_t *obj) {
const char *snd_ctl_card_info_get_id(const snd_ctl_card_info_t *obj) {
return "TestId";
}
+int snd_hctl_open(snd_hctl_t **hctlp, const char *name, int mode) {
+ *hctlp = snd_hctl_open_pointer_val;
+ snd_hctl_open_called++;
+ return snd_hctl_open_return_value;
+}
+int snd_hctl_nonblock(snd_hctl_t *hctl, int nonblock) {
+ snd_hctl_nonblock_called++;
+ return 0;
+}
+int snd_hctl_load(snd_hctl_t *hctl) {
+ snd_hctl_load_called++;
+ return snd_hctl_load_return_value;
+}
+int snd_hctl_close(snd_hctl_t *hctl) {
+ snd_hctl_close_called++;
+ return 0;
+}
+int snd_hctl_poll_descriptors_count(snd_hctl_t *hctl) {
+ return snd_hctl_poll_descriptors_num_fds;
+}
+int snd_hctl_poll_descriptors(snd_hctl_t *hctl,
+ struct pollfd *pfds,
+ unsigned int space) {
+ unsigned int num = MIN(space, snd_hctl_poll_descriptors_num_fds);
+ memcpy(pfds, snd_hctl_poll_descriptors_fds, num * sizeof(*pfds));
+ snd_hctl_poll_descriptors_called++;
+ return num;
+}
+int snd_hctl_handle_events(snd_hctl_t *hctl) {
+ snd_hctl_handle_events_called++;
+ return 0;
+}
+
+int cras_system_add_select_fd(int fd,
+ void (*callback)(void *data),
+ void *callback_data)
+{
+ cras_system_add_select_fd_called++;
+ cras_system_add_select_fd_values.push_back(fd);
+ return 0;
+}
+void cras_system_rm_select_fd(int fd)
+{
+ cras_system_rm_select_fd_called++;
+ cras_system_rm_select_fd_values.push_back(fd);
+}
struct cras_card_config *cras_card_config_create(const char *config_path,
const char *card_name)
@@ -473,32 +1025,69 @@ int cras_device_blacklist_check(
return cras_device_blacklist_check_retval;
}
-snd_use_case_mgr_t* ucm_create(const char* name) {
+struct cras_use_case_mgr *ucm_create(const char* name) {
ucm_create_called++;
- return reinterpret_cast<snd_use_case_mgr_t*>(0x44);
+ return reinterpret_cast<struct cras_use_case_mgr *>(0x44);
}
-void ucm_destroy(snd_use_case_mgr_t* mgr) {
+void ucm_destroy(struct cras_use_case_mgr *mgr) {
ucm_destroy_called++;
}
-char *ucm_get_dev_for_mixer(snd_use_case_mgr_t *mgr, const char *mixer)
+char *ucm_get_dev_for_mixer(struct cras_use_case_mgr *mgr, const char *mixer,
+ enum CRAS_STREAM_DIRECTION dir)
{
ucm_get_dev_for_mixer_called++;
return strdup("device");
}
-char *ucm_get_flag(snd_use_case_mgr_t *mgr, const char *flag_name) {
+char *ucm_get_flag(struct cras_use_case_mgr *mgr, const char *flag_name) {
ucm_get_flag_called++;
strncpy(ucm_get_flag_name, flag_name, sizeof(ucm_get_flag_name));
return NULL;
}
+struct mixer_name *ucm_get_coupled_mixer_names(
+ struct cras_use_case_mgr *mgr, const char *dev)
+{
+ return ucm_get_coupled_mixer_names_return_value;
+}
+
+int ucm_has_fully_specified_ucm_flag(struct cras_use_case_mgr *mgr)
+{
+ return ucm_has_fully_specified_ucm_flag_return_value;
+}
+
+struct ucm_section *ucm_get_sections(struct cras_use_case_mgr *mgr)
+{
+ ucm_get_sections_called++;
+ return ucm_get_sections_return_value;
+}
+
+int cras_alsa_mixer_add_controls_in_section(
+ struct cras_alsa_mixer *cmix,
+ struct ucm_section *section)
+{
+ cras_alsa_mixer_add_controls_in_section_called++;
+ return cras_alsa_mixer_add_controls_in_section_return_value;
+}
+
+void ucm_free_mixer_names(struct mixer_name *names)
+{
+ struct mixer_name *m;
+ DL_FOREACH(names, m) {
+ DL_DELETE(names, m);
+ free((void*)m->name);
+ free(m);
+ }
+}
+
} /* extern "C" */
} // namespace
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
+ openlog(NULL, LOG_PERROR, LOG_USER);
return RUN_ALL_TESTS();
}
diff --git a/cras/src/tests/alsa_helpers_unittest.cc b/cras/src/tests/alsa_helpers_unittest.cc
index 6cb68051..f774a509 100644
--- a/cras/src/tests/alsa_helpers_unittest.cc
+++ b/cras/src/tests/alsa_helpers_unittest.cc
@@ -3,12 +3,28 @@
// found in the LICENSE file.
#include <gtest/gtest.h>
+#include <vector>
extern "C" {
// For static function test.
#include "cras_alsa_helpers.c"
}
+static int snd_pcm_sw_params_set_tstamp_type_called;
+static int snd_pcm_sw_params_set_tstamp_mode_called;
+static snd_pcm_uframes_t snd_pcm_htimestamp_avail_ret_val;
+static timespec snd_pcm_htimestamp_tstamp_ret_val;
+static std::vector<int> snd_pcm_sw_params_ret_vals;
+
+static void ResetStubData() {
+ snd_pcm_sw_params_set_tstamp_type_called = 0;
+ snd_pcm_sw_params_set_tstamp_mode_called = 0;
+ snd_pcm_htimestamp_avail_ret_val = 0;
+ snd_pcm_htimestamp_tstamp_ret_val.tv_sec = 0;
+ snd_pcm_htimestamp_tstamp_ret_val.tv_nsec = 0;
+ snd_pcm_sw_params_ret_vals.clear();
+}
+
namespace {
static snd_pcm_chmap_query_t *create_chmap_cap(snd_pcm_chmap_type type,
@@ -147,8 +163,167 @@ TEST(AlsaHelper, MatchChannelMapCapability51) {
cras_audio_format_destroy(fmt);
}
+TEST(AlsaHelper, Htimestamp) {
+ snd_pcm_t *dummy_handle = reinterpret_cast<snd_pcm_t*>(0x1);
+ snd_pcm_uframes_t used;
+ snd_pcm_uframes_t severe_underrun_frames = 480;
+ struct timespec tstamp;
+ unsigned int underruns = 0;
+ int htimestamp_enabled = 1;
+ const char *dev_name = "dev_name";
+
+ // Enable htimestamp use.
+ ResetStubData();
+ EXPECT_EQ(0, cras_alsa_set_swparams(dummy_handle, &htimestamp_enabled));
+ EXPECT_EQ(snd_pcm_sw_params_set_tstamp_mode_called, 1);
+ EXPECT_EQ(snd_pcm_sw_params_set_tstamp_type_called, 1);
+ EXPECT_EQ(1, htimestamp_enabled);
+
+ // Try to enable htimestamp use: not supported.
+ ResetStubData();
+ snd_pcm_sw_params_ret_vals.push_back(-EINVAL);
+ EXPECT_EQ(0, cras_alsa_set_swparams(dummy_handle, &htimestamp_enabled));
+ EXPECT_EQ(snd_pcm_sw_params_set_tstamp_mode_called, 2);
+ EXPECT_EQ(snd_pcm_sw_params_set_tstamp_type_called, 2);
+ EXPECT_EQ(0, htimestamp_enabled);
+
+ // Disable htimestamp use.
+ ResetStubData();
+ EXPECT_EQ(0, cras_alsa_set_swparams(dummy_handle, &htimestamp_enabled));
+ EXPECT_EQ(snd_pcm_sw_params_set_tstamp_mode_called, 0);
+ EXPECT_EQ(snd_pcm_sw_params_set_tstamp_type_called, 0);
+
+ ResetStubData();
+ tstamp.tv_sec = 0;
+ tstamp.tv_nsec = 0;
+ snd_pcm_htimestamp_avail_ret_val = 20000;
+ snd_pcm_htimestamp_tstamp_ret_val.tv_sec = 10;
+ snd_pcm_htimestamp_tstamp_ret_val.tv_nsec = 10000;
+
+ cras_alsa_get_avail_frames(dummy_handle, 48000, severe_underrun_frames,
+ dev_name, &used, &tstamp, &underruns);
+ EXPECT_EQ(used, snd_pcm_htimestamp_avail_ret_val);
+ EXPECT_EQ(tstamp.tv_sec, snd_pcm_htimestamp_tstamp_ret_val.tv_sec);
+ EXPECT_EQ(tstamp.tv_nsec, snd_pcm_htimestamp_tstamp_ret_val.tv_nsec);
+}
+
+TEST(AlsaHelper, GetAvailFramesSevereUnderrun) {
+ snd_pcm_t *dummy_handle = reinterpret_cast<snd_pcm_t*>(0x1);
+ snd_pcm_uframes_t avail;
+ snd_pcm_uframes_t severe_underrun_frames = 480;
+ snd_pcm_uframes_t buffer_size = 48000;
+ struct timespec tstamp;
+ unsigned int underruns = 0;
+ int rc;
+ const char *dev_name = "dev_name";
+
+ ResetStubData();
+ snd_pcm_htimestamp_avail_ret_val = buffer_size + severe_underrun_frames + 1;
+ rc = cras_alsa_get_avail_frames(dummy_handle, buffer_size,
+ severe_underrun_frames, dev_name,
+ &avail, &tstamp, &underruns);
+ // Returns -EPIPE when severe underrun happens.
+ EXPECT_EQ(rc, -EPIPE);
+ EXPECT_EQ(1, underruns);
+
+ ResetStubData();
+ snd_pcm_htimestamp_avail_ret_val = buffer_size + severe_underrun_frames;
+ rc = cras_alsa_get_avail_frames(dummy_handle, buffer_size,
+ severe_underrun_frames, dev_name,
+ &avail, &tstamp, &underruns);
+ // Underrun which is not severe enough will be masked.
+ // avail will be adjusted to buffer_size.
+ EXPECT_EQ(avail, buffer_size);
+ EXPECT_EQ(rc, 0);
+ EXPECT_EQ(2, underruns);
+
+ ResetStubData();
+ snd_pcm_htimestamp_avail_ret_val = buffer_size;
+ rc = cras_alsa_get_avail_frames(dummy_handle, buffer_size,
+ severe_underrun_frames, dev_name,
+ &avail, &tstamp, &underruns);
+ // When avail == buffer_size, num_underruns will be increased.
+ EXPECT_EQ(avail, buffer_size);
+ EXPECT_EQ(rc, 0);
+ EXPECT_EQ(3, underruns);
+
+ ResetStubData();
+ snd_pcm_htimestamp_avail_ret_val = buffer_size - 1;
+ rc = cras_alsa_get_avail_frames(dummy_handle, buffer_size,
+ severe_underrun_frames, dev_name,
+ &avail, &tstamp, &underruns);
+ // When avail < buffer_size, there is no underrun.
+ EXPECT_EQ(avail, buffer_size - 1);
+ EXPECT_EQ(rc, 0);
+ EXPECT_EQ(3, underruns);
+}
} // namespace
+extern "C" {
+
+int snd_pcm_sw_params_current(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) {
+ return 0;
+}
+
+int snd_pcm_sw_params_get_boundary(const snd_pcm_sw_params_t *params,
+ snd_pcm_uframes_t *val) {
+ return 0;
+}
+
+int snd_pcm_sw_params_set_stop_threshold(snd_pcm_t *pcm,
+ snd_pcm_sw_params_t *params,
+ snd_pcm_uframes_t val) {
+ return 0;
+}
+
+int snd_pcm_sw_params_set_start_threshold(snd_pcm_t *pcm,
+ snd_pcm_sw_params_t *params,
+ snd_pcm_uframes_t val) {
+ return 0;
+}
+
+int snd_pcm_sw_params_set_period_event(snd_pcm_t *pcm,
+ snd_pcm_sw_params_t *params, int val) {
+ return 0;
+}
+
+int snd_pcm_sw_params_set_tstamp_mode(snd_pcm_t *pcm,
+ snd_pcm_sw_params_t *params,
+ snd_pcm_tstamp_t val) {
+ snd_pcm_sw_params_set_tstamp_mode_called++;
+ return 0;
+}
+
+int snd_pcm_sw_params_set_tstamp_type(snd_pcm_t *pcm,
+ snd_pcm_sw_params_t *params,
+ snd_pcm_tstamp_type_t val) {
+ snd_pcm_sw_params_set_tstamp_type_called++;
+ return 0;
+}
+
+int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) {
+ int rc;
+
+ if (snd_pcm_sw_params_ret_vals.size() == 0)
+ return 0;
+ rc = snd_pcm_sw_params_ret_vals.back();
+ snd_pcm_sw_params_ret_vals.pop_back();
+ return rc;
+}
+
+snd_pcm_sframes_t snd_pcm_avail(snd_pcm_t *pcm) {
+ return snd_pcm_htimestamp_avail_ret_val;
+}
+
+int snd_pcm_htimestamp(snd_pcm_t *pcm, snd_pcm_uframes_t *avail,
+ snd_htimestamp_t *tstamp) {
+ *avail = snd_pcm_htimestamp_avail_ret_val;
+ *tstamp = snd_pcm_htimestamp_tstamp_ret_val;
+ return 0;
+}
+
+}
+
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
diff --git a/cras/src/tests/alsa_io_unittest.cc b/cras/src/tests/alsa_io_unittest.cc
index 6edf9ba2..dc58cd75 100644
--- a/cras/src/tests/alsa_io_unittest.cc
+++ b/cras/src/tests/alsa_io_unittest.cc
@@ -2,8 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <stdio.h>
#include <gtest/gtest.h>
+#include <map>
+#include <stdio.h>
+#include <syslog.h>
+#include <vector>
extern "C" {
@@ -17,6 +20,8 @@ extern "C" {
#include "cras_alsa_io.c"
}
+#define BUFFER_SIZE 8192
+
// Data for simulating functions stubbed below.
static int cras_alsa_open_called;
static int cras_iodev_append_stream_ret;
@@ -38,6 +43,9 @@ static const struct mixer_control
*cras_alsa_mixer_get_maximum_capture_gain_mixer_input;
static size_t cras_alsa_mixer_list_outputs_called;
static size_t cras_alsa_mixer_list_inputs_called;
+static size_t cras_alsa_mixer_get_control_for_section_called;
+static struct mixer_control *
+ cras_alsa_mixer_get_control_for_section_return_value;
static size_t sys_get_volume_called;
static size_t sys_get_volume_return_value;
static size_t sys_get_capture_gain_called;
@@ -57,6 +65,7 @@ static int sys_get_mute_return_value;
static size_t sys_get_capture_mute_called;
static int sys_get_capture_mute_return_value;
static struct cras_alsa_mixer *fake_mixer = (struct cras_alsa_mixer *)1;
+static struct cras_card_config *fake_config = (struct cras_card_config *)2;
static struct mixer_control **cras_alsa_mixer_list_outputs_outputs;
static size_t cras_alsa_mixer_list_outputs_outputs_length;
static struct mixer_control **cras_alsa_mixer_list_inputs_outputs;
@@ -65,25 +74,36 @@ static size_t cras_alsa_mixer_set_output_active_state_called;
static std::vector<struct mixer_control *>
cras_alsa_mixer_set_output_active_state_outputs;
static std::vector<int> cras_alsa_mixer_set_output_active_state_values;
-static size_t cras_alsa_mixer_default_volume_curve_called;
-static cras_volume_curve *fake_curve;
static cras_audio_format *fake_format;
static size_t sys_set_volume_limits_called;
static size_t sys_set_capture_gain_limits_called;
static size_t cras_alsa_mixer_get_minimum_capture_gain_called;
static size_t cras_alsa_mixer_get_maximum_capture_gain_called;
+static struct mixer_control *cras_alsa_jack_get_mixer_output_ret;
+static struct mixer_control *cras_alsa_jack_get_mixer_input_ret;
static size_t cras_alsa_mixer_get_output_volume_curve_called;
-static struct cras_volume_curve *cras_alsa_mixer_get_output_volume_curve_value;
+typedef std::map<const struct mixer_control*, std::string> ControlNameMap;
+static ControlNameMap cras_alsa_mixer_get_control_name_values;
+static size_t cras_alsa_mixer_get_control_name_called;
static size_t cras_alsa_jack_list_create_called;
+static size_t cras_alsa_jack_list_find_jacks_by_name_matching_called;
+static size_t cras_alsa_jack_list_add_jack_for_section_called;
+static struct cras_alsa_jack *
+ cras_alsa_jack_list_add_jack_for_section_result_jack;
static size_t cras_alsa_jack_list_destroy_called;
+static int cras_alsa_jack_list_has_hctl_jacks_return_val;
static jack_state_change_callback *cras_alsa_jack_list_create_cb;
static void *cras_alsa_jack_list_create_cb_data;
static char test_card_name[] = "TestCard";
static char test_dev_name[] = "TestDev";
+static char test_dev_id[] = "TestDevId";
+static size_t cras_iodev_add_node_called;
+static struct cras_ionode *cras_iodev_set_node_attr_ionode;
static size_t cras_iodev_set_node_attr_called;
static enum ionode_attr cras_iodev_set_node_attr_attr;
static int cras_iodev_set_node_attr_value;
static unsigned cras_alsa_jack_enable_ucm_called;
+static unsigned ucm_set_enabled_called;
static size_t cras_iodev_update_dsp_called;
static const char *cras_iodev_update_dsp_name;
static size_t ucm_get_dsp_name_default_called;
@@ -97,9 +117,36 @@ static int ucm_enable_swap_mode_ret_value;
static size_t ucm_enable_swap_mode_called;
static int is_utf8_string_ret_value;
static char *cras_alsa_jack_update_monitor_fake_name = 0;
-static int cras_alsa_jack_get_name_ret_called;
+static int cras_alsa_jack_get_name_called;
static const char *cras_alsa_jack_get_name_ret_value = 0;
static char default_jack_name[] = "Something Jack";
+static int auto_unplug_input_node_ret = 0;
+static int auto_unplug_output_node_ret = 0;
+static int ucm_get_max_software_gain_called;
+static int ucm_get_max_software_gain_ret_value;
+static long ucm_get_max_software_gain_value;
+static long cras_system_set_capture_gain_limits_set_value[2];
+static long cras_alsa_mixer_get_minimum_capture_gain_ret_value;
+static long cras_alsa_mixer_get_maximum_capture_gain_ret_value;
+static snd_pcm_state_t snd_pcm_state_ret;
+static int cras_alsa_attempt_resume_called;
+static snd_hctl_t *fake_hctl = (snd_hctl_t *)2;
+static size_t ucm_get_dma_period_for_dev_called;
+static unsigned int ucm_get_dma_period_for_dev_ret;
+static int cras_card_config_get_volume_curve_for_control_called;
+typedef std::map<std::string, struct cras_volume_curve *> VolCurveMap;
+static VolCurveMap cras_card_config_get_volume_curve_vals;
+static int cras_alsa_mmap_get_whole_buffer_called;
+static int cras_iodev_fill_odev_zeros_called;
+static unsigned int cras_iodev_fill_odev_zeros_frames;
+static int cras_iodev_frames_queued_ret;
+static int cras_iodev_buffer_avail_ret;
+static int cras_alsa_resume_appl_ptr_called;
+static int cras_alsa_resume_appl_ptr_ahead;
+static int ucm_get_enable_htimestamp_flag_ret;
+static const struct cras_volume_curve *fake_get_dBFS_volume_curve_val;
+static int cras_iodev_dsp_set_swap_mode_for_node_called;
+static std::map<std::string, long> ucm_get_default_node_gain_values;
void ResetStubData() {
cras_alsa_open_called = 0;
@@ -118,6 +165,8 @@ void ResetStubData() {
alsa_mixer_get_dB_range_called = 0;
alsa_mixer_get_output_dB_range_called = 0;
alsa_mixer_set_capture_mute_called = 0;
+ cras_alsa_mixer_get_control_for_section_called = 0;
+ cras_alsa_mixer_get_control_for_section_return_value = NULL;
cras_alsa_mixer_list_outputs_called = 0;
cras_alsa_mixer_list_outputs_outputs_length = 0;
cras_alsa_mixer_list_inputs_called = 0;
@@ -125,17 +174,26 @@ void ResetStubData() {
cras_alsa_mixer_set_output_active_state_called = 0;
cras_alsa_mixer_set_output_active_state_outputs.clear();
cras_alsa_mixer_set_output_active_state_values.clear();
- cras_alsa_mixer_default_volume_curve_called = 0;
sys_set_volume_limits_called = 0;
sys_set_capture_gain_limits_called = 0;
+ sys_get_capture_gain_return_value = 0;
cras_alsa_mixer_get_minimum_capture_gain_called = 0;
cras_alsa_mixer_get_maximum_capture_gain_called = 0;
cras_alsa_mixer_get_output_volume_curve_called = 0;
- cras_alsa_mixer_get_output_volume_curve_value = NULL;
+ cras_alsa_jack_get_mixer_output_ret = NULL;
+ cras_alsa_jack_get_mixer_input_ret = NULL;
+ cras_alsa_mixer_get_control_name_values.clear();
+ cras_alsa_mixer_get_control_name_called = 0;
cras_alsa_jack_list_create_called = 0;
+ cras_alsa_jack_list_find_jacks_by_name_matching_called = 0;
+ cras_alsa_jack_list_add_jack_for_section_called = 0;
+ cras_alsa_jack_list_add_jack_for_section_result_jack = NULL;
cras_alsa_jack_list_destroy_called = 0;
+ cras_alsa_jack_list_has_hctl_jacks_return_val = 1;
+ cras_iodev_add_node_called = 0;
cras_iodev_set_node_attr_called = 0;
cras_alsa_jack_enable_ucm_called = 0;
+ ucm_set_enabled_called = 0;
cras_iodev_update_dsp_called = 0;
cras_iodev_update_dsp_name = 0;
ucm_get_dsp_name_default_called = 0;
@@ -148,14 +206,57 @@ void ResetStubData() {
ucm_enable_swap_mode_ret_value = 0;
ucm_enable_swap_mode_called = 0;
is_utf8_string_ret_value = 1;
- cras_alsa_jack_get_name_ret_called = 0;
+ cras_alsa_jack_get_name_called = 0;
cras_alsa_jack_get_name_ret_value = default_jack_name;
cras_alsa_jack_update_monitor_fake_name = 0;
+ ucm_get_max_software_gain_called = 0;
+ ucm_get_max_software_gain_ret_value = -1;
+ ucm_get_max_software_gain_value = 0;
+ cras_card_config_get_volume_curve_for_control_called = 0;
+ cras_card_config_get_volume_curve_vals.clear();
+ cras_system_set_capture_gain_limits_set_value[0] = -1;
+ cras_system_set_capture_gain_limits_set_value[1] = -1;
+ cras_alsa_mixer_get_minimum_capture_gain_ret_value = 0;
+ cras_alsa_mixer_get_maximum_capture_gain_ret_value = 0;
+ snd_pcm_state_ret = SND_PCM_STATE_RUNNING;
+ cras_alsa_attempt_resume_called = 0;
+ ucm_get_dma_period_for_dev_called = 0;
+ ucm_get_dma_period_for_dev_ret = 0;
+ cras_alsa_mmap_get_whole_buffer_called = 0;
+ cras_iodev_fill_odev_zeros_called = 0;
+ cras_iodev_fill_odev_zeros_frames = 0;
+ cras_iodev_frames_queued_ret = 0;
+ cras_iodev_buffer_avail_ret = 0;
+ cras_alsa_resume_appl_ptr_called = 0;
+ cras_alsa_resume_appl_ptr_ahead = 0;
+ ucm_get_enable_htimestamp_flag_ret = 0;
+ fake_get_dBFS_volume_curve_val = NULL;
+ cras_iodev_dsp_set_swap_mode_for_node_called = 0;
+ ucm_get_default_node_gain_values.clear();
+}
+
+static long fake_get_dBFS(const struct cras_volume_curve *curve, size_t volume)
+{
+ fake_get_dBFS_volume_curve_val = curve;
+ return (volume - 100) * 100;
}
+static cras_volume_curve default_curve = {
+ .get_dBFS = fake_get_dBFS,
+};
-static long fake_get_dBFS(const cras_volume_curve *curve, size_t volume)
-{
- return (volume - 100) * 100;
+static struct cras_iodev *alsa_iodev_create_with_default_parameters(
+ size_t card_index,
+ const char *dev_id,
+ enum CRAS_ALSA_CARD_TYPE card_type,
+ int is_first,
+ struct cras_alsa_mixer *mixer,
+ struct cras_card_config *config,
+ struct cras_use_case_mgr *ucm,
+ enum CRAS_STREAM_DIRECTION direction) {
+ return alsa_iodev_create(card_index, test_card_name, 0, test_dev_name,
+ dev_id, card_type, is_first,
+ mixer, config, ucm, fake_hctl,
+ direction, 0, 0, (char *)"123");
}
namespace {
@@ -163,10 +264,9 @@ namespace {
TEST(AlsaIoInit, InitializeInvalidDirection) {
struct alsa_io *aio;
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 0,
- fake_mixer, NULL,
- CRAS_NUM_DIRECTIONS, 0, 0);
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, NULL,
+ CRAS_NUM_DIRECTIONS);
ASSERT_EQ(aio, (void *)NULL);
}
@@ -175,19 +275,24 @@ TEST(AlsaIoInit, InitializePlayback) {
struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2;
ResetStubData();
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 1,
- fake_mixer, NULL,
- CRAS_STREAM_OUTPUT, 0, 0);
- ASSERT_NE(aio, (void *)NULL);
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, test_dev_id, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, NULL,
+ CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
+ /* Get volume curve twice for iodev, and default node. */
+ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream);
- EXPECT_EQ(1, cras_alsa_fill_properties_called);
+ EXPECT_EQ(0, cras_alsa_fill_properties_called);
EXPECT_EQ(1, cras_alsa_mixer_list_outputs_called);
EXPECT_EQ(0, strncmp(test_card_name,
aio->base.info.name,
strlen(test_card_name)));
EXPECT_EQ(0, ucm_get_dsp_name_default_called);
EXPECT_EQ(NULL, cras_iodev_update_dsp_name);
+ ASSERT_NE(reinterpret_cast<const char *>(NULL), aio->dev_name);
+ EXPECT_EQ(0, strcmp(test_dev_name, aio->dev_name));
+ ASSERT_NE(reinterpret_cast<const char *>(NULL), aio->dev_id);
+ EXPECT_EQ(0, strcmp(test_dev_id, aio->dev_id));
alsa_iodev_destroy((struct cras_iodev *)aio);
EXPECT_EQ(1, cras_iodev_free_resources_called);
@@ -198,40 +303,49 @@ TEST(AlsaIoInit, DefaultNodeInternalCard) {
struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2;
ResetStubData();
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 0,
- fake_mixer, NULL,
- CRAS_STREAM_OUTPUT, 0, 0);
-
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, NULL,
+ CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
+ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
ASSERT_STREQ("(default)", aio->base.active_node->name);
ASSERT_EQ(1, aio->base.active_node->plugged);
+ ASSERT_EQ((void *)no_stream, (void *)aio->base.no_stream);
+ ASSERT_EQ((void *)output_should_wake, (void *)aio->base.output_should_wake);
alsa_iodev_destroy((struct cras_iodev *)aio);
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 1,
- fake_mixer, NULL,
- CRAS_STREAM_OUTPUT, 0, 0);
-
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, NULL,
+ CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
+ EXPECT_EQ(4, cras_card_config_get_volume_curve_for_control_called);
ASSERT_STREQ("Speaker", aio->base.active_node->name);
ASSERT_EQ(1, aio->base.active_node->plugged);
+ ASSERT_EQ((void *)no_stream, (void *)aio->base.no_stream);
+ ASSERT_EQ((void *)output_should_wake, (void *)aio->base.output_should_wake);
alsa_iodev_destroy((struct cras_iodev *)aio);
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 0,
- fake_mixer, NULL,
- CRAS_STREAM_INPUT, 0, 0);
-
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, NULL,
+ CRAS_STREAM_INPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
+ /* No more call to get volume curve for input device. */
+ EXPECT_EQ(4, cras_card_config_get_volume_curve_for_control_called);
ASSERT_STREQ("(default)", aio->base.active_node->name);
ASSERT_EQ(1, aio->base.active_node->plugged);
+ ASSERT_EQ((void *)no_stream, (void *)aio->base.no_stream);
+ ASSERT_EQ((void *)output_should_wake, (void *)aio->base.output_should_wake);
alsa_iodev_destroy((struct cras_iodev *)aio);
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 1,
- fake_mixer, NULL,
- CRAS_STREAM_INPUT, 0, 0);
-
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, NULL,
+ CRAS_STREAM_INPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
+ EXPECT_EQ(4, cras_card_config_get_volume_curve_for_control_called);
ASSERT_STREQ("Internal Mic", aio->base.active_node->name);
ASSERT_EQ(1, aio->base.active_node->plugged);
+ ASSERT_EQ((void *)no_stream, (void *)aio->base.no_stream);
+ ASSERT_EQ((void *)output_should_wake, (void *)aio->base.output_should_wake);
alsa_iodev_destroy((struct cras_iodev *)aio);
}
@@ -240,11 +354,11 @@ TEST(AlsaIoInit, DefaultNodeUSBCard) {
struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2;
ResetStubData();
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_USB, 1,
- fake_mixer, NULL,
- CRAS_STREAM_OUTPUT, 0, 0);
-
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_USB, 1, fake_mixer, fake_config, NULL,
+ CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
+ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
ASSERT_STREQ("(default)", aio->base.active_node->name);
ASSERT_EQ(1, aio->base.active_node->plugged);
EXPECT_EQ(1, cras_iodev_set_node_attr_called);
@@ -252,11 +366,11 @@ TEST(AlsaIoInit, DefaultNodeUSBCard) {
EXPECT_EQ(1, cras_iodev_set_node_attr_value);
alsa_iodev_destroy((struct cras_iodev *)aio);
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_USB, 1,
- fake_mixer, NULL,
- CRAS_STREAM_INPUT, 0, 0);
-
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_USB, 1, fake_mixer, fake_config, NULL,
+ CRAS_STREAM_INPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
+ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
ASSERT_STREQ("(default)", aio->base.active_node->name);
ASSERT_EQ(1, aio->base.active_node->plugged);
EXPECT_EQ(2, cras_iodev_set_node_attr_called);
@@ -268,28 +382,34 @@ TEST(AlsaIoInit, DefaultNodeUSBCard) {
TEST(AlsaIoInit, OpenPlayback) {
struct cras_iodev *iodev;
struct cras_audio_format format;
+ struct alsa_io *aio;
ResetStubData();
- iodev = alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 0,
- fake_mixer, NULL,
- CRAS_STREAM_OUTPUT, 0, 0);
-
+ iodev = alsa_iodev_create_with_default_parameters(0, NULL,
+ ALSA_CARD_TYPE_INTERNAL, 0,
+ fake_mixer, fake_config,
+ NULL, CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
+ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
+ aio = (struct alsa_io *)iodev;
+ format.frame_rate = 48000;
cras_iodev_set_format(iodev, &format);
- fake_curve =
- static_cast<struct cras_volume_curve *>(calloc(1, sizeof(*fake_curve)));
- fake_curve->get_dBFS = fake_get_dBFS;
+ // Test that these flags are cleared after open_dev.
+ aio->is_free_running = 1;
+ aio->filled_zeros_for_draining = 512;
iodev->open_dev(iodev);
EXPECT_EQ(1, cras_alsa_open_called);
EXPECT_EQ(1, sys_set_volume_limits_called);
EXPECT_EQ(1, alsa_mixer_set_dBFS_called);
EXPECT_EQ(0, cras_alsa_start_called);
EXPECT_EQ(0, cras_iodev_set_node_attr_called);
+ EXPECT_EQ(0, aio->is_free_running);
+ EXPECT_EQ(0, aio->filled_zeros_for_draining);
+ EXPECT_EQ(SEVERE_UNDERRUN_MS * format.frame_rate / 1000,
+ aio->severe_underrun_frames);
alsa_iodev_destroy(iodev);
- free(fake_curve);
- fake_curve = NULL;
free(fake_format);
}
@@ -297,26 +417,30 @@ TEST(AlsaIoInit, UsbCardAutoPlug) {
struct cras_iodev *iodev;
ResetStubData();
- iodev = alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 1,
- fake_mixer, NULL,
- CRAS_STREAM_OUTPUT, 0, 0);
+ iodev = alsa_iodev_create_with_default_parameters(0, NULL,
+ ALSA_CARD_TYPE_INTERNAL, 1,
+ fake_mixer, fake_config,
+ NULL, CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
+ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
EXPECT_EQ(0, cras_iodev_set_node_attr_called);
alsa_iodev_destroy(iodev);
ResetStubData();
- iodev = alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_USB, 0,
- fake_mixer, NULL,
- CRAS_STREAM_OUTPUT, 0, 0);
+ iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_USB,
+ 0, fake_mixer, fake_config,
+ NULL, CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
+ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
EXPECT_EQ(0, cras_iodev_set_node_attr_called);
alsa_iodev_destroy(iodev);
ResetStubData();
- iodev = alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_USB, 1,
- fake_mixer, NULL,
- CRAS_STREAM_OUTPUT, 0, 0);
+ iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_USB,
+ 1, fake_mixer, fake_config,
+ NULL, CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
+ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
// Should assume USB devs are plugged when they appear.
EXPECT_EQ(1, cras_iodev_set_node_attr_called);
EXPECT_EQ(IONODE_ATTR_PLUGGED, cras_iodev_set_node_attr_attr);
@@ -330,10 +454,11 @@ TEST(AlsaIoInit, UsbCardUseSoftwareVolume) {
alsa_mixer_get_dB_range_value = 1000;
alsa_mixer_get_output_dB_range_value = 1000;
ResetStubData();
- iodev = alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_USB, 1,
- fake_mixer, NULL,
- CRAS_STREAM_OUTPUT, 0, 0);
+ iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_USB,
+ 1, fake_mixer, fake_config,
+ NULL, CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
+ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
EXPECT_EQ(1, alsa_mixer_get_dB_range_called);
EXPECT_EQ(1, alsa_mixer_get_output_dB_range_called);
EXPECT_EQ(1, iodev->active_node->software_volume_needed);
@@ -342,34 +467,112 @@ TEST(AlsaIoInit, UsbCardUseSoftwareVolume) {
alsa_mixer_get_dB_range_value = 3000;
alsa_mixer_get_output_dB_range_value = 2000;
ResetStubData();
- iodev = alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_USB, 1,
- fake_mixer, NULL,
- CRAS_STREAM_OUTPUT, 0, 0);
+ iodev = alsa_iodev_create_with_default_parameters(0, NULL, ALSA_CARD_TYPE_USB,
+ 1, fake_mixer, fake_config,
+ NULL, CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
+ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
EXPECT_EQ(1, alsa_mixer_get_dB_range_called);
EXPECT_EQ(1, alsa_mixer_get_output_dB_range_called);
EXPECT_EQ(0, iodev->active_node->software_volume_needed);
alsa_iodev_destroy(iodev);
}
+TEST(AlsaIoInit, UseSoftwareGain) {
+ struct cras_iodev *iodev;
+ struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3;
+
+ /* Meet the requirements of using software gain. */
+ ResetStubData();
+ ucm_get_max_software_gain_ret_value = 0;
+ ucm_get_max_software_gain_value = 2000;
+ iodev = alsa_iodev_create_with_default_parameters(0, NULL,
+ ALSA_CARD_TYPE_INTERNAL, 1,
+ fake_mixer, fake_config,
+ fake_ucm,
+ CRAS_STREAM_INPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
+ EXPECT_EQ(1, iodev->active_node->software_volume_needed);
+ EXPECT_EQ(2000, iodev->active_node->max_software_gain);
+ ASSERT_EQ(1, sys_set_capture_gain_limits_called);
+ /* The gain range is [DEFAULT_MIN_CAPTURE_GAIN, maximum softare gain]. */
+ ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[0],
+ DEFAULT_MIN_CAPTURE_GAIN);
+ ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[1], 2000);
+
+ /* MaxSoftwareGain is not specified in UCM */
+ ResetStubData();
+ ucm_get_max_software_gain_ret_value = 1;
+ ucm_get_max_software_gain_value = 1;
+ cras_alsa_mixer_get_minimum_capture_gain_ret_value = -500;
+ cras_alsa_mixer_get_maximum_capture_gain_ret_value = 500;
+ iodev = alsa_iodev_create_with_default_parameters(0, NULL,
+ ALSA_CARD_TYPE_INTERNAL, 1,
+ fake_mixer, fake_config,
+ fake_ucm,
+ CRAS_STREAM_INPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
+ EXPECT_EQ(0, iodev->active_node->software_volume_needed);
+ EXPECT_EQ(0, iodev->active_node->max_software_gain);
+ ASSERT_EQ(1, sys_set_capture_gain_limits_called);
+ /* The gain range is reported by controls. */
+ ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[0], -500);
+ ASSERT_EQ(cras_system_set_capture_gain_limits_set_value[1], 500);
+
+ alsa_iodev_destroy(iodev);
+}
+
+TEST(AlsaIoInit, SoftwareGainWithDefaultNodeGain) {
+ struct cras_iodev *iodev;
+ struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3;
+ long system_gain = 500;
+ long default_node_gain = -1000;
+
+ ResetStubData();
+
+ // Use software gain.
+ ucm_get_max_software_gain_ret_value = 0;
+ ucm_get_max_software_gain_value = 2000;
+
+ // Set default node gain to -1000 dBm.
+ ucm_get_default_node_gain_values["Internal Mic"] = default_node_gain;
+
+ // Assume this is the first device so it gets internal mic node name.
+ iodev = alsa_iodev_create_with_default_parameters(0, NULL,
+ ALSA_CARD_TYPE_INTERNAL, 1,
+ fake_mixer, fake_config,
+ fake_ucm,
+ CRAS_STREAM_INPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
+
+ // Gain on node is 300 dBm.
+ iodev->active_node->capture_gain = default_node_gain;
+
+ // cras_iodev will call cras_iodev_adjust_active_node_gain to get gain for
+ // software gain.
+ ASSERT_EQ(system_gain + default_node_gain,
+ cras_iodev_adjust_active_node_gain(iodev, system_gain));
+
+ alsa_iodev_destroy(iodev);
+}
+
TEST(AlsaIoInit, RouteBasedOnJackCallback) {
struct alsa_io *aio;
struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2;
ResetStubData();
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 1,
- fake_mixer, NULL,
- CRAS_STREAM_OUTPUT, 0, 0);
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, NULL,
+ CRAS_STREAM_OUTPUT);
ASSERT_NE(aio, (void *)NULL);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
+ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream);
- EXPECT_EQ(1, cras_alsa_fill_properties_called);
+ EXPECT_EQ(0, cras_alsa_fill_properties_called);
EXPECT_EQ(1, cras_alsa_mixer_list_outputs_called);
EXPECT_EQ(1, cras_alsa_jack_list_create_called);
-
- fake_curve =
- static_cast<struct cras_volume_curve *>(calloc(1, sizeof(*fake_curve)));
- fake_curve->get_dBFS = fake_get_dBFS;
+ EXPECT_EQ(1, cras_alsa_jack_list_find_jacks_by_name_matching_called);
+ EXPECT_EQ(0, cras_alsa_jack_list_add_jack_for_section_called);
cras_alsa_jack_list_create_cb(NULL, 1, cras_alsa_jack_list_create_cb_data);
EXPECT_EQ(1, cras_iodev_set_node_attr_called);
@@ -382,8 +585,6 @@ TEST(AlsaIoInit, RouteBasedOnJackCallback) {
alsa_iodev_destroy((struct cras_iodev *)aio);
EXPECT_EQ(1, cras_alsa_jack_list_destroy_called);
- free(fake_curve);
- fake_curve = NULL;
}
TEST(AlsaIoInit, RouteBasedOnInputJackCallback) {
@@ -391,18 +592,17 @@ TEST(AlsaIoInit, RouteBasedOnInputJackCallback) {
struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2;
ResetStubData();
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 0,
- fake_mixer, NULL,
- CRAS_STREAM_INPUT, 0, 0);
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, NULL,
+ CRAS_STREAM_INPUT);
ASSERT_NE(aio, (void *)NULL);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
+
EXPECT_EQ(SND_PCM_STREAM_CAPTURE, aio->alsa_stream);
- EXPECT_EQ(1, cras_alsa_fill_properties_called);
+ EXPECT_EQ(0, cras_alsa_fill_properties_called);
EXPECT_EQ(1, cras_alsa_jack_list_create_called);
-
- fake_curve =
- static_cast<struct cras_volume_curve *>(calloc(1, sizeof(*fake_curve)));
- fake_curve->get_dBFS = fake_get_dBFS;
+ EXPECT_EQ(1, cras_alsa_jack_list_find_jacks_by_name_matching_called);
+ EXPECT_EQ(0, cras_alsa_jack_list_add_jack_for_section_called);
cras_alsa_jack_list_create_cb(NULL, 1, cras_alsa_jack_list_create_cb_data);
EXPECT_EQ(1, cras_iodev_set_node_attr_called);
@@ -415,21 +615,20 @@ TEST(AlsaIoInit, RouteBasedOnInputJackCallback) {
alsa_iodev_destroy((struct cras_iodev *)aio);
EXPECT_EQ(1, cras_alsa_jack_list_destroy_called);
- free(fake_curve);
- fake_curve = NULL;
}
TEST(AlsaIoInit, InitializeCapture) {
struct alsa_io *aio;
ResetStubData();
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 1,
- fake_mixer, NULL,
- CRAS_STREAM_INPUT, 0, 0);
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, NULL,
+ CRAS_STREAM_INPUT);
ASSERT_NE(aio, (void *)NULL);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
+
EXPECT_EQ(SND_PCM_STREAM_CAPTURE, aio->alsa_stream);
- EXPECT_EQ(1, cras_alsa_fill_properties_called);
+ EXPECT_EQ(0, cras_alsa_fill_properties_called);
EXPECT_EQ(1, cras_alsa_mixer_list_inputs_called);
alsa_iodev_destroy((struct cras_iodev *)aio);
@@ -438,12 +637,16 @@ TEST(AlsaIoInit, InitializeCapture) {
TEST(AlsaIoInit, OpenCapture) {
struct cras_iodev *iodev;
struct cras_audio_format format;
+ struct alsa_io *aio;
- iodev = alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 0,
- fake_mixer, NULL,
- CRAS_STREAM_INPUT, 0, 0);
+ iodev = alsa_iodev_create_with_default_parameters(0, NULL,
+ ALSA_CARD_TYPE_INTERNAL, 0,
+ fake_mixer, fake_config,
+ NULL, CRAS_STREAM_INPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
+ aio = (struct alsa_io *)iodev;
+ format.frame_rate = 48000;
cras_iodev_set_format(iodev, &format);
ResetStubData();
@@ -457,6 +660,84 @@ TEST(AlsaIoInit, OpenCapture) {
EXPECT_EQ(1, sys_get_capture_mute_called);
EXPECT_EQ(1, alsa_mixer_set_capture_mute_called);
EXPECT_EQ(1, cras_alsa_start_called);
+ EXPECT_EQ(SEVERE_UNDERRUN_MS * format.frame_rate / 1000,
+ aio->severe_underrun_frames);
+
+ alsa_iodev_destroy(iodev);
+ free(fake_format);
+}
+
+TEST(AlsaIoInit, OpenCaptureSetCaptureGainWithDefaultNodeGain) {
+ struct cras_iodev *iodev;
+ struct cras_audio_format format;
+ struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3;
+ long system_gain = 2000;
+ long default_node_gain = -1000;
+
+ ResetStubData();
+ // Set default node gain to -1000 dBm.
+ ucm_get_default_node_gain_values["Internal Mic"] = default_node_gain;
+
+ // Assume this is the first device so it gets internal mic node name.
+ iodev = alsa_iodev_create_with_default_parameters(0, NULL,
+ ALSA_CARD_TYPE_INTERNAL, 1,
+ fake_mixer, fake_config,
+ fake_ucm,
+ CRAS_STREAM_INPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
+
+ cras_iodev_set_format(iodev, &format);
+
+ // Check the default node gain is the same as what specified in UCM.
+ EXPECT_EQ(default_node_gain, iodev->active_node->capture_gain);
+ // System gain is set to 2000 dBm.
+ sys_get_capture_gain_return_value = system_gain;
+
+ iodev->open_dev(iodev);
+ iodev->close_dev(iodev);
+
+ // Hardware gain is set to 2000 - 1000 dBm.
+ EXPECT_EQ(system_gain + default_node_gain, alsa_mixer_set_capture_dBFS_value);
+
+ alsa_iodev_destroy(iodev);
+ free(fake_format);
+}
+
+TEST(AlsaIoInit, OpenCaptureSetCaptureGainWithSoftwareGain) {
+ struct cras_iodev *iodev;
+ struct cras_audio_format format;
+ struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3;
+
+ /* Meet the requirements of using software gain. */
+ ResetStubData();
+ ucm_get_max_software_gain_ret_value = 0;
+ ucm_get_max_software_gain_value = 2000;
+
+ iodev = alsa_iodev_create_with_default_parameters(0, NULL,
+ ALSA_CARD_TYPE_INTERNAL, 0,
+ fake_mixer, fake_config,
+ fake_ucm,
+ CRAS_STREAM_INPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
+
+ cras_iodev_set_format(iodev, &format);
+
+ /* System gain is set to 1000dBm */
+ sys_get_capture_gain_return_value = 1000;
+
+ iodev->open_dev(iodev);
+ iodev->close_dev(iodev);
+
+ /* Hardware gain is set to 0dB when software gain is used. */
+ EXPECT_EQ(0, alsa_mixer_set_capture_dBFS_value);
+
+ /* Test the case where software gain is not needed. */
+ iodev->active_node->software_volume_needed = 0;
+ iodev->open_dev(iodev);
+ iodev->close_dev(iodev);
+
+ /* Hardware gain is set to 1000dBm as got from system capture gain.*/
+ EXPECT_EQ(1000, alsa_mixer_set_capture_dBFS_value);
alsa_iodev_destroy(iodev);
free(fake_format);
@@ -467,12 +748,63 @@ TEST(AlsaIoInit, UpdateActiveNode) {
struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2;
ResetStubData();
- iodev = alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 0,
- fake_mixer, NULL,
- CRAS_STREAM_OUTPUT, 0, 0);
+ iodev = alsa_iodev_create_with_default_parameters(0, NULL,
+ ALSA_CARD_TYPE_INTERNAL, 0,
+ fake_mixer, fake_config,
+ NULL,
+ CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
+ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
- iodev->update_active_node(iodev, 0);
+ iodev->update_active_node(iodev, 0, 1);
+
+ alsa_iodev_destroy(iodev);
+}
+
+TEST(AlsaIoInit, StartDevice) {
+ struct cras_iodev *iodev;
+ int rc;
+
+ ResetStubData();
+ iodev = alsa_iodev_create_with_default_parameters(0, NULL,
+ ALSA_CARD_TYPE_INTERNAL, 0,
+ NULL, fake_config, NULL,
+ CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
+ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
+
+ // Return right away if it is already running.
+ snd_pcm_state_ret = SND_PCM_STATE_RUNNING;
+ rc = iodev->start(iodev);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_alsa_start_called);
+
+ // Otherwise, start the device.
+ snd_pcm_state_ret = SND_PCM_STATE_SETUP;
+ rc = iodev->start(iodev);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, cras_alsa_start_called);
+
+ alsa_iodev_destroy(iodev);
+}
+
+TEST(AlsaIoInit, ResumeDevice) {
+ struct cras_iodev *iodev;
+ int rc;
+
+ ResetStubData();
+ iodev = alsa_iodev_create_with_default_parameters(0, NULL,
+ ALSA_CARD_TYPE_INTERNAL, 0,
+ NULL, fake_config, NULL,
+ CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init(iodev));
+ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
+
+ // Attempt to resume if the device is suspended.
+ snd_pcm_state_ret = SND_PCM_STATE_SUSPENDED;
+ rc = iodev->start(iodev);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, cras_alsa_attempt_resume_called);
alsa_iodev_destroy(iodev);
}
@@ -480,15 +812,15 @@ TEST(AlsaIoInit, UpdateActiveNode) {
TEST(AlsaIoInit, DspNameDefault) {
struct alsa_io *aio;
struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2;
- snd_use_case_mgr_t * const fake_ucm = (snd_use_case_mgr_t*)3;
+ struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3;
ResetStubData();
ucm_get_dsp_name_default_value = "hello";
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 0,
- fake_mixer, fake_ucm,
- CRAS_STREAM_OUTPUT, 0, 0);
- ASSERT_NE(aio, (void *)NULL);
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, fake_ucm,
+ CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
+ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream);
EXPECT_EQ(1, ucm_get_dsp_name_default_called);
EXPECT_EQ(1, cras_alsa_jack_get_dsp_name_called);
@@ -500,17 +832,16 @@ TEST(AlsaIoInit, DspNameDefault) {
TEST(AlsaIoInit, DspNameJackOverride) {
struct alsa_io *aio;
struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2;
- snd_use_case_mgr_t * const fake_ucm = (snd_use_case_mgr_t*)3;
+ struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3;
const struct cras_alsa_jack *jack = (struct cras_alsa_jack*)4;
ResetStubData();
ucm_get_dsp_name_default_value = "default_dsp";
cras_alsa_jack_get_dsp_name_value = "override_dsp";
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 0,
- fake_mixer, fake_ucm,
- CRAS_STREAM_OUTPUT, 0, 0);
- ASSERT_NE(aio, (void *)NULL);
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, fake_ucm,
+ CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream);
EXPECT_EQ(1, ucm_get_dsp_name_default_called);
EXPECT_EQ(1, cras_alsa_jack_get_dsp_name_called);
@@ -522,13 +853,13 @@ TEST(AlsaIoInit, DspNameJackOverride) {
EXPECT_EQ(1, ucm_get_dsp_name_default_called);
// Mark the jack node as active.
- alsa_iodev_set_active_node(&aio->base, aio->base.nodes->next);
+ alsa_iodev_set_active_node(&aio->base, aio->base.nodes->next, 1);
EXPECT_EQ(2, cras_alsa_jack_get_dsp_name_called);
EXPECT_EQ(2, cras_iodev_update_dsp_called);
EXPECT_STREQ("override_dsp", cras_iodev_update_dsp_name);
// Mark the default node as active.
- alsa_iodev_set_active_node(&aio->base, aio->base.nodes);
+ alsa_iodev_set_active_node(&aio->base, aio->base.nodes, 1);
EXPECT_EQ(1, ucm_get_dsp_name_default_called);
EXPECT_EQ(3, cras_alsa_jack_get_dsp_name_called);
EXPECT_EQ(3, cras_iodev_update_dsp_called);
@@ -540,15 +871,14 @@ TEST(AlsaIoInit, DspNameJackOverride) {
TEST(AlsaIoInit, NodeTypeOverride) {
struct alsa_io *aio;
struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2;
- snd_use_case_mgr_t * const fake_ucm = (snd_use_case_mgr_t*)3;
+ struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3;
const struct cras_alsa_jack *jack = (struct cras_alsa_jack*)4;
ResetStubData();
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 0,
- fake_mixer, fake_ucm,
- CRAS_STREAM_OUTPUT, 0, 0);
- ASSERT_NE(aio, (void *)NULL);
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, fake_ucm,
+ CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
// Add the jack node.
cras_alsa_jack_list_create_cb(jack, 1, cras_alsa_jack_list_create_cb_data);
// Verify that cras_alsa_jack_update_node_type is called when an output device
@@ -561,27 +891,28 @@ TEST(AlsaIoInit, NodeTypeOverride) {
TEST(AlsaIoInit, SwapMode) {
struct alsa_io *aio;
struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2;
- snd_use_case_mgr_t * const fake_ucm = (snd_use_case_mgr_t*)3;
+ struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3;
struct cras_ionode * const fake_node = (cras_ionode *)4;
ResetStubData();
// Stub replies that swap mode does not exist.
ucm_swap_mode_exists_ret_value = 0;
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 0,
- fake_mixer, fake_ucm,
- CRAS_STREAM_OUTPUT, 0, 0);
- ASSERT_NE(aio, (void *)NULL);
- EXPECT_EQ(NULL, aio->base.set_swap_mode_for_node);
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, fake_ucm,
+ CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
+
+ aio->base.set_swap_mode_for_node((cras_iodev*)aio, fake_node, 1);
+ /* Swap mode is implemented by dsp. */
+ EXPECT_EQ(1, cras_iodev_dsp_set_swap_mode_for_node_called);
// Stub replies that swap mode exists.
ucm_swap_mode_exists_ret_value = 1;
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 0,
- fake_mixer, fake_ucm,
- CRAS_STREAM_OUTPUT, 0, 0);
- ASSERT_NE(aio, (void *)NULL);
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, fake_ucm,
+ CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
// Enable swap mode.
aio->base.set_swap_mode_for_node((cras_iodev*)aio, fake_node, 1);
@@ -602,23 +933,20 @@ TEST(AlsaOutputNode, SystemSettingsWhenInactive) {
ResetStubData();
outputs[0] = reinterpret_cast<struct mixer_control *>(3);
outputs[1] = reinterpret_cast<struct mixer_control *>(4);
- fake_curve =
- static_cast<struct cras_volume_curve *>(calloc(1, sizeof(*fake_curve)));
- fake_curve->get_dBFS = fake_get_dBFS;
- cras_alsa_mixer_get_output_volume_curve_value = fake_curve;
cras_alsa_mixer_list_outputs_outputs = outputs;
cras_alsa_mixer_list_outputs_outputs_length = ARRAY_SIZE(outputs);
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 1,
- fake_mixer, NULL,
- CRAS_STREAM_OUTPUT, 0, 0);
- ASSERT_NE(aio, (void *)NULL);
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, NULL,
+ CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
+ /* Two mixer controls calls get volume curve. */
+ EXPECT_EQ(4, cras_card_config_get_volume_curve_for_control_called);
EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream);
EXPECT_EQ(1, cras_alsa_mixer_list_outputs_called);
ResetStubData();
rc = alsa_iodev_set_active_node((struct cras_iodev *)aio,
- aio->base.nodes->next);
+ aio->base.nodes->next, 1);
EXPECT_EQ(0, rc);
EXPECT_EQ(0, alsa_mixer_set_mute_called);
EXPECT_EQ(0, alsa_mixer_set_dBFS_called);
@@ -628,11 +956,11 @@ TEST(AlsaOutputNode, SystemSettingsWhenInactive) {
EXPECT_EQ(outputs[1], cras_alsa_mixer_set_output_active_state_outputs[1]);
EXPECT_EQ(1, cras_alsa_mixer_set_output_active_state_values[1]);
EXPECT_EQ(1, cras_iodev_update_dsp_called);
- EXPECT_EQ(2, cras_alsa_jack_enable_ucm_called);
+ // No jack is defined, and UCM is not used.
+ EXPECT_EQ(0, cras_alsa_jack_enable_ucm_called);
+ EXPECT_EQ(0, ucm_set_enabled_called);
alsa_iodev_destroy((struct cras_iodev *)aio);
- free(fake_curve);
- fake_curve = NULL;
}
// Test handling of different amounts of outputs.
@@ -645,29 +973,164 @@ TEST(AlsaOutputNode, TwoOutputs) {
ResetStubData();
outputs[0] = reinterpret_cast<struct mixer_control *>(3);
outputs[1] = reinterpret_cast<struct mixer_control *>(4);
- fake_curve =
- static_cast<struct cras_volume_curve *>(calloc(1, sizeof(*fake_curve)));
- fake_curve->get_dBFS = fake_get_dBFS;
- cras_alsa_mixer_get_output_volume_curve_value = fake_curve;
cras_alsa_mixer_list_outputs_outputs = outputs;
cras_alsa_mixer_list_outputs_outputs_length = ARRAY_SIZE(outputs);
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 1,
- fake_mixer, NULL,
- CRAS_STREAM_OUTPUT, 0, 0);
- ASSERT_NE(aio, (void *)NULL);
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, NULL,
+ CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
+ EXPECT_EQ(4, cras_card_config_get_volume_curve_for_control_called);
EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream);
EXPECT_EQ(1, cras_alsa_mixer_list_outputs_called);
- // This will be called three times because there will be
- // two default node (because the output control's name is "")
- // and one speaker node (because it is the first internal device).
- EXPECT_EQ(3, cras_alsa_mixer_get_output_volume_curve_called);
aio->handle = (snd_pcm_t *)0x24;
ResetStubData();
rc = alsa_iodev_set_active_node((struct cras_iodev *)aio,
- aio->base.nodes->next);
+ aio->base.nodes->next, 1);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(2, alsa_mixer_set_mute_called);
+ EXPECT_EQ(outputs[1], alsa_mixer_set_mute_output);
+ EXPECT_EQ(1, alsa_mixer_set_dBFS_called);
+ EXPECT_EQ(outputs[1], alsa_mixer_set_dBFS_output);
+ ASSERT_EQ(2, cras_alsa_mixer_set_output_active_state_called);
+ EXPECT_EQ(outputs[0], cras_alsa_mixer_set_output_active_state_outputs[0]);
+ EXPECT_EQ(0, cras_alsa_mixer_set_output_active_state_values[0]);
+ EXPECT_EQ(outputs[1], cras_alsa_mixer_set_output_active_state_outputs[1]);
+ EXPECT_EQ(1, cras_alsa_mixer_set_output_active_state_values[1]);
+ EXPECT_EQ(1, cras_iodev_update_dsp_called);
+ // No jacks defined, and UCM is not used.
+ EXPECT_EQ(0, cras_alsa_jack_enable_ucm_called);
+ EXPECT_EQ(0, ucm_set_enabled_called);
+
+ alsa_iodev_destroy((struct cras_iodev *)aio);
+}
+
+TEST(AlsaOutputNode, TwoJacksHeadphoneLineout) {
+ struct alsa_io *aio;
+ struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer *)2;
+ struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr *)3;
+ struct cras_iodev *iodev;
+ struct mixer_control *output;
+ struct ucm_section *section;
+
+ ResetStubData();
+ output = reinterpret_cast<struct mixer_control *>(3);
+ cras_alsa_mixer_get_control_name_values[output] = "Headphone";
+
+ // Create the iodev
+ iodev = alsa_iodev_create_with_default_parameters(
+ 0, 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);
+ EXPECT_EQ(1, cras_card_config_get_volume_curve_for_control_called);
+
+ // First node 'Headphone'
+ section = ucm_section_create("Headphone", 0, CRAS_STREAM_OUTPUT,
+ "fake-jack", "gpio");
+ ucm_section_set_mixer_name(section, "Headphone");
+ cras_alsa_jack_list_add_jack_for_section_result_jack =
+ reinterpret_cast<struct cras_alsa_jack *>(10);
+ cras_alsa_mixer_get_control_for_section_return_value = output;
+ ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section));
+ EXPECT_EQ(4, cras_card_config_get_volume_curve_for_control_called);
+ ucm_section_free_list(section);
+
+ // Second node 'Line Out'
+ section = ucm_section_create("Line Out", 0, CRAS_STREAM_OUTPUT,
+ "fake-jack", "gpio");
+ ucm_section_set_mixer_name(section, "Headphone");
+ cras_alsa_jack_list_add_jack_for_section_result_jack =
+ reinterpret_cast<struct cras_alsa_jack *>(20);
+ cras_alsa_mixer_get_control_for_section_return_value = output;
+ ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section));
+ EXPECT_EQ(7, cras_card_config_get_volume_curve_for_control_called);
+ ucm_section_free_list(section);
+
+ // Both nodes are associated with the same mixer output. Different jack plug
+ // report should trigger different node attribute change.
+ cras_alsa_jack_get_mixer_output_ret = output;
+ jack_output_plug_event(reinterpret_cast<struct cras_alsa_jack *>(10), 0, aio);
+ EXPECT_STREQ(cras_iodev_set_node_attr_ionode->name, "Headphone");
+
+ jack_output_plug_event(reinterpret_cast<struct cras_alsa_jack *>(20), 0, aio);
+ EXPECT_STREQ(cras_iodev_set_node_attr_ionode->name, "Line Out");
+
+ alsa_iodev_destroy(iodev);
+}
+
+TEST(AlsaOutputNode, OutputsFromUCM) {
+ struct alsa_io *aio;
+ struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2;
+ struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3;
+ struct cras_iodev *iodev;
+ static const char *jack_name = "TestCard - Headset Jack";
+ struct mixer_control *outputs[2];
+ int rc;
+ struct ucm_section *section;
+
+ ResetStubData();
+ outputs[0] = reinterpret_cast<struct mixer_control *>(3);
+ outputs[1] = reinterpret_cast<struct mixer_control *>(4);
+ cras_alsa_mixer_list_outputs_outputs = outputs;
+ cras_alsa_mixer_list_outputs_outputs_length = ARRAY_SIZE(outputs);
+ cras_alsa_mixer_get_control_name_values[outputs[0]] = INTERNAL_SPEAKER;
+ cras_alsa_mixer_get_control_name_values[outputs[1]] = "Headphone";
+ ucm_get_dma_period_for_dev_ret = 1000;
+
+ // Create the IO device.
+ iodev = alsa_iodev_create_with_default_parameters(0, 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);
+ EXPECT_EQ(1, cras_card_config_get_volume_curve_for_control_called);
+
+ // First node.
+ section = ucm_section_create(INTERNAL_SPEAKER, 0, CRAS_STREAM_OUTPUT,
+ NULL, NULL);
+ ucm_section_set_mixer_name(section, INTERNAL_SPEAKER);
+ cras_alsa_jack_list_add_jack_for_section_result_jack =
+ reinterpret_cast<struct cras_alsa_jack *>(1);
+ cras_alsa_mixer_get_control_for_section_return_value = outputs[0];
+ ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section));
+ ucm_section_free_list(section);
+ EXPECT_EQ(4, cras_card_config_get_volume_curve_for_control_called);
+
+ // Add a second node (will use the same iodev).
+ section = ucm_section_create("Headphone", 0, CRAS_STREAM_OUTPUT,
+ jack_name, "hctl");
+ ucm_section_add_coupled(section, "HP-L", MIXER_NAME_VOLUME);
+ ucm_section_add_coupled(section, "HP-R", MIXER_NAME_VOLUME);
+ cras_alsa_jack_list_add_jack_for_section_result_jack = NULL;
+ cras_alsa_mixer_get_control_for_section_return_value = outputs[1];
+ ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section));
+ ucm_section_free_list(section);
+ /* New nodes creation calls get volume curve once, NULL jack doesn't make
+ * more calls. */
+ EXPECT_EQ(5, cras_card_config_get_volume_curve_for_control_called);
+
+ // Jack plug of an unkonwn device should do nothing.
+ cras_alsa_jack_get_mixer_output_ret = NULL;
+ cras_alsa_jack_get_name_ret_value = "Some other jack";
+ jack_output_plug_event(reinterpret_cast<struct cras_alsa_jack *>(4), 0, aio);
+ EXPECT_EQ(0, cras_iodev_set_node_attr_called);
+
+ // Complete initialization, and make first node active.
+ alsa_iodev_ucm_complete_init(iodev);
+ EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream);
+ EXPECT_EQ(2, cras_alsa_jack_list_add_jack_for_section_called);
+ EXPECT_EQ(2, cras_alsa_mixer_get_control_for_section_called);
+ EXPECT_EQ(1, ucm_get_dma_period_for_dev_called);
+ EXPECT_EQ(ucm_get_dma_period_for_dev_ret, aio->dma_period_set_microsecs);
+
+ aio->handle = (snd_pcm_t *)0x24;
+
+ ResetStubData();
+ rc = alsa_iodev_set_active_node(iodev, aio->base.nodes->next, 1);
EXPECT_EQ(0, rc);
EXPECT_EQ(2, alsa_mixer_set_mute_called);
EXPECT_EQ(outputs[1], alsa_mixer_set_mute_output);
@@ -679,11 +1142,360 @@ TEST(AlsaOutputNode, TwoOutputs) {
EXPECT_EQ(outputs[1], cras_alsa_mixer_set_output_active_state_outputs[1]);
EXPECT_EQ(1, cras_alsa_mixer_set_output_active_state_values[1]);
EXPECT_EQ(1, cras_iodev_update_dsp_called);
- EXPECT_EQ(2, cras_alsa_jack_enable_ucm_called);
+ EXPECT_EQ(1, cras_alsa_jack_enable_ucm_called);
+ EXPECT_EQ(1, ucm_set_enabled_called);
+
+ // Simulate jack plug event.
+ cras_alsa_jack_get_mixer_output_ret = outputs[1];
+ cras_alsa_jack_get_name_ret_value = jack_name;
+ jack_output_plug_event(reinterpret_cast<struct cras_alsa_jack *>(4), 0, aio);
+ EXPECT_EQ(1, cras_iodev_set_node_attr_called);
+
+ alsa_iodev_destroy(iodev);
+}
+
+TEST(AlsaOutputNode, OutputNoControlsUCM) {
+ 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;
+
+ ResetStubData();
+
+ // Create the IO device.
+ iodev = alsa_iodev_create_with_default_parameters(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);
+ EXPECT_EQ(1, cras_card_config_get_volume_curve_for_control_called);
+
+ // Node without controls or jacks.
+ section = ucm_section_create(INTERNAL_SPEAKER, 1, CRAS_STREAM_OUTPUT,
+ NULL, NULL);
+ // Device index doesn't match.
+ EXPECT_EQ(-22, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section));
+ section->dev_idx = 0;
+ ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section));
+ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
+ EXPECT_EQ(1, cras_alsa_mixer_get_control_for_section_called);
+ EXPECT_EQ(1, cras_iodev_add_node_called);
+ ucm_section_free_list(section);
+
+ // Complete initialization, and make first node active.
+ alsa_iodev_ucm_complete_init(iodev);
+ EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream);
+ EXPECT_EQ(0, cras_alsa_mixer_get_control_name_called);
+ EXPECT_EQ(1, cras_iodev_update_dsp_called);
+ EXPECT_EQ(0, cras_alsa_jack_enable_ucm_called);
+ EXPECT_EQ(1, ucm_set_enabled_called);
+
+ alsa_iodev_destroy(iodev);
+}
+
+TEST(AlsaOutputNode, OutputFromJackUCM) {
+ struct alsa_io *aio;
+ struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3;
+ struct cras_iodev *iodev;
+ static const char *jack_name = "TestCard - Headset Jack";
+ struct ucm_section *section;
+
+ ResetStubData();
+
+ // Create the IO device.
+ iodev = alsa_iodev_create_with_default_parameters(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);
+ EXPECT_EQ(1, cras_card_config_get_volume_curve_for_control_called);
+
+ // Node without controls or jacks.
+ cras_alsa_jack_list_add_jack_for_section_result_jack =
+ reinterpret_cast<struct cras_alsa_jack *>(1);
+ section = ucm_section_create("Headphone", 0, CRAS_STREAM_OUTPUT,
+ jack_name, "hctl");
+ ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section));
+ EXPECT_EQ(4, cras_card_config_get_volume_curve_for_control_called);
+ EXPECT_EQ(1, cras_alsa_mixer_get_control_for_section_called);
+ EXPECT_EQ(1, cras_iodev_add_node_called);
+ EXPECT_EQ(1, cras_alsa_jack_list_add_jack_for_section_called);
+ ucm_section_free_list(section);
+
+ // Complete initialization, and make first node active.
+ alsa_iodev_ucm_complete_init(iodev);
+ EXPECT_EQ(SND_PCM_STREAM_PLAYBACK, aio->alsa_stream);
+ EXPECT_EQ(0, cras_alsa_mixer_get_control_name_called);
+ EXPECT_EQ(1, cras_iodev_update_dsp_called);
+ EXPECT_EQ(1, cras_alsa_jack_enable_ucm_called);
+ EXPECT_EQ(0, ucm_set_enabled_called);
+
+ alsa_iodev_destroy(iodev);
+}
+
+TEST(AlsaOutputNode, InputsFromUCM) {
+ struct alsa_io *aio;
+ struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2;
+ struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3;
+ struct mixer_control *inputs[2];
+ struct cras_iodev *iodev;
+ static const char *jack_name = "TestCard - Headset Jack";
+ int rc;
+ struct ucm_section *section;
+
+ ResetStubData();
+ inputs[0] = reinterpret_cast<struct mixer_control *>(3);
+ inputs[1] = reinterpret_cast<struct mixer_control *>(4);
+ cras_alsa_mixer_list_inputs_outputs = inputs;
+ cras_alsa_mixer_list_inputs_outputs_length = ARRAY_SIZE(inputs);
+ cras_alsa_mixer_get_control_name_values[inputs[0]] = "Internal Mic";
+ cras_alsa_mixer_get_control_name_values[inputs[1]] = "Mic";
+
+ // Create the IO device.
+ iodev = alsa_iodev_create_with_default_parameters(0, NULL,
+ ALSA_CARD_TYPE_INTERNAL, 1,
+ fake_mixer, fake_config,
+ fake_ucm,
+ CRAS_STREAM_INPUT);
+ ASSERT_NE(iodev, (void *)NULL);
+ aio = reinterpret_cast<struct alsa_io *>(iodev);
+
+ // First node.
+ cras_alsa_mixer_get_control_for_section_return_value = inputs[0];
+ ucm_get_max_software_gain_ret_value = -1;
+ section = ucm_section_create(INTERNAL_MICROPHONE, 0, CRAS_STREAM_INPUT,
+ NULL, NULL);
+ ucm_section_add_coupled(section, "MIC-L", MIXER_NAME_VOLUME);
+ ucm_section_add_coupled(section, "MIC-R", MIXER_NAME_VOLUME);
+ ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section));
+ ucm_section_free_list(section);
+
+ // Add a second node (will use the same iodev).
+ cras_alsa_mixer_get_control_name_called = 0;
+ ucm_get_max_software_gain_ret_value = 0;
+ ucm_get_max_software_gain_value = 2000;
+ cras_alsa_jack_list_add_jack_for_section_result_jack =
+ reinterpret_cast<struct cras_alsa_jack *>(1);
+ cras_alsa_mixer_get_control_for_section_return_value = inputs[1];
+ section = ucm_section_create("Mic", 0, CRAS_STREAM_INPUT, jack_name, "hctl");
+ ucm_section_set_mixer_name(section, "Mic");
+ ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section));
+ ucm_section_free_list(section);
+
+ // Jack plug of an unkonwn device should do nothing.
+ cras_alsa_jack_get_mixer_input_ret = NULL;
+ cras_alsa_jack_get_name_ret_value = "Some other jack";
+ jack_input_plug_event(reinterpret_cast<struct cras_alsa_jack *>(4), 0, aio);
+ EXPECT_EQ(0, cras_iodev_set_node_attr_called);
+
+ // Simulate jack plug event.
+ cras_alsa_jack_get_mixer_input_ret = inputs[1];
+ cras_alsa_jack_get_name_ret_value = jack_name;
+ jack_input_plug_event(reinterpret_cast<struct cras_alsa_jack *>(4), 0, aio);
+ EXPECT_EQ(1, cras_iodev_set_node_attr_called);
+
+ // Complete initialization, and make first node active.
+ alsa_iodev_ucm_complete_init(iodev);
+ EXPECT_EQ(SND_PCM_STREAM_CAPTURE, aio->alsa_stream);
+ EXPECT_EQ(2, cras_alsa_jack_list_add_jack_for_section_called);
+ EXPECT_EQ(2, cras_alsa_mixer_get_control_for_section_called);
+ EXPECT_EQ(1, cras_alsa_mixer_get_control_name_called);
+ EXPECT_EQ(1, sys_set_capture_gain_limits_called);
+ EXPECT_EQ(2, cras_iodev_add_node_called);
+ EXPECT_EQ(2, ucm_get_dma_period_for_dev_called);
+ EXPECT_EQ(0, aio->dma_period_set_microsecs);
+
+ aio->handle = (snd_pcm_t *)0x24;
+
+ ResetStubData();
+ rc = alsa_iodev_set_active_node(iodev, aio->base.nodes->next, 1);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, alsa_mixer_set_capture_dBFS_called);
+ EXPECT_EQ(inputs[1], alsa_mixer_set_capture_dBFS_input);
+ EXPECT_EQ(0, alsa_mixer_set_capture_dBFS_value);
+ EXPECT_EQ(1, cras_iodev_update_dsp_called);
+ EXPECT_EQ(1, cras_alsa_jack_enable_ucm_called);
+ EXPECT_EQ(1, ucm_set_enabled_called);
+ EXPECT_EQ(1, sys_set_capture_gain_limits_called);
+ EXPECT_EQ(1, alsa_mixer_set_capture_mute_called);
+ EXPECT_EQ(1, iodev->active_node->software_volume_needed);
+ EXPECT_EQ(2000, iodev->active_node->max_software_gain);
+
+ alsa_iodev_destroy(iodev);
+}
+
+TEST(AlsaOutputNode, InputNoControlsUCM) {
+ 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;
+
+ ResetStubData();
+
+ // Create the IO device.
+ iodev = alsa_iodev_create_with_default_parameters(1, NULL,
+ ALSA_CARD_TYPE_INTERNAL, 1,
+ fake_mixer, fake_config,
+ fake_ucm,
+ CRAS_STREAM_INPUT);
+ ASSERT_NE(iodev, (void *)NULL);
+ aio = reinterpret_cast<struct alsa_io *>(iodev);
+
+ // Node without controls or jacks.
+ section = ucm_section_create(INTERNAL_MICROPHONE, 1, CRAS_STREAM_INPUT,
+ NULL, NULL);
+ // Device index doesn't match.
+ EXPECT_EQ(-22, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section));
+ section->dev_idx = 0;
+ ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section));
+ EXPECT_EQ(1, cras_alsa_jack_list_add_jack_for_section_called);
+ EXPECT_EQ(1, cras_alsa_mixer_get_control_for_section_called);
+ EXPECT_EQ(0, cras_alsa_mixer_get_control_name_called);
+ EXPECT_EQ(1, cras_iodev_add_node_called);
+ ucm_section_free_list(section);
+
+ // Complete initialization, and make first node active.
+ alsa_iodev_ucm_complete_init(iodev);
+ EXPECT_EQ(SND_PCM_STREAM_CAPTURE, aio->alsa_stream);
+ EXPECT_EQ(0, cras_alsa_mixer_get_control_name_called);
+ EXPECT_EQ(1, cras_iodev_update_dsp_called);
+ EXPECT_EQ(0, cras_alsa_jack_enable_ucm_called);
+ EXPECT_EQ(1, ucm_set_enabled_called);
+
+ alsa_iodev_destroy(iodev);
+}
+
+TEST(AlsaOutputNode, InputFromJackUCM) {
+ struct alsa_io *aio;
+ struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3;
+ struct cras_iodev *iodev;
+ static const char *jack_name = "TestCard - Headset Jack";
+ struct ucm_section *section;
+
+ ResetStubData();
+
+ // Create the IO device.
+ iodev = alsa_iodev_create_with_default_parameters(1, NULL,
+ ALSA_CARD_TYPE_INTERNAL, 1,
+ fake_mixer, fake_config,
+ fake_ucm,
+ CRAS_STREAM_INPUT);
+ ASSERT_NE(iodev, (void *)NULL);
+ aio = reinterpret_cast<struct alsa_io *>(iodev);
+
+ // Node without controls or jacks.
+ cras_alsa_jack_list_add_jack_for_section_result_jack =
+ reinterpret_cast<struct cras_alsa_jack *>(1);
+ section = ucm_section_create("Mic", 0, CRAS_STREAM_INPUT, jack_name, "hctl");
+ ASSERT_EQ(0, alsa_iodev_ucm_add_nodes_and_jacks(iodev, section));
+ EXPECT_EQ(1, cras_alsa_mixer_get_control_for_section_called);
+ EXPECT_EQ(1, cras_iodev_add_node_called);
+ EXPECT_EQ(1, cras_alsa_jack_list_add_jack_for_section_called);
+ ucm_section_free_list(section);
+
+ // Complete initialization, and make first node active.
+ alsa_iodev_ucm_complete_init(iodev);
+ EXPECT_EQ(SND_PCM_STREAM_CAPTURE, aio->alsa_stream);
+ EXPECT_EQ(0, cras_alsa_mixer_get_control_name_called);
+ EXPECT_EQ(1, cras_iodev_update_dsp_called);
+ EXPECT_EQ(1, cras_alsa_jack_enable_ucm_called);
+ EXPECT_EQ(0, ucm_set_enabled_called);
+
+ alsa_iodev_destroy(iodev);
+}
+
+TEST(AlsaOutputNode, AutoUnplugOutputNode) {
+ struct alsa_io *aio;
+ struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2;
+ struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3;
+ struct mixer_control *outputs[2];
+ const struct cras_alsa_jack *jack = (struct cras_alsa_jack*)4;
+
+ ResetStubData();
+ outputs[0] = reinterpret_cast<struct mixer_control *>(5);
+ outputs[1] = reinterpret_cast<struct mixer_control *>(6);
+
+ cras_alsa_mixer_list_outputs_outputs = outputs;
+ cras_alsa_mixer_list_outputs_outputs_length = ARRAY_SIZE(outputs);
+
+ cras_alsa_mixer_get_control_name_values[outputs[0]] = INTERNAL_SPEAKER;
+ cras_alsa_mixer_get_control_name_values[outputs[1]] = "Headphone";
+ auto_unplug_output_node_ret = 1;
+
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm,
+ CRAS_STREAM_OUTPUT);
+
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
+ EXPECT_EQ(3, cras_card_config_get_volume_curve_for_control_called);
+ EXPECT_EQ(1, cras_alsa_mixer_list_outputs_called);
+ EXPECT_EQ(2, cras_alsa_mixer_get_control_name_called);
+
+ // Assert that the the internal speaker is plugged and other nodes aren't.
+ ASSERT_NE(aio->base.nodes, (void *)NULL);
+ EXPECT_EQ(aio->base.nodes->plugged, 1);
+ ASSERT_NE(aio->base.nodes->next, (void *)NULL);
+ EXPECT_EQ(aio->base.nodes->next->plugged, 0);
+
+ // Plug headphone jack
+ cras_alsa_jack_get_name_ret_value = "Headphone Jack";
+ is_utf8_string_ret_value = 1;
+ cras_alsa_jack_get_mixer_output_ret = outputs[1];
+ cras_alsa_jack_list_create_cb(jack, 1, cras_alsa_jack_list_create_cb_data);
+
+ // Assert internal speaker is auto unplugged
+ EXPECT_EQ(aio->base.nodes->plugged, 0);
+ EXPECT_EQ(aio->base.nodes->next->plugged, 1);
+
+ alsa_iodev_destroy((struct cras_iodev *)aio);
+}
+
+TEST(AlsaOutputNode, AutoUnplugInputNode) {
+ struct alsa_io *aio;
+ struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2;
+ struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3;
+ struct mixer_control *inputs[2];
+ const struct cras_alsa_jack *jack = (struct cras_alsa_jack*)4;
+
+ ResetStubData();
+ inputs[0] = reinterpret_cast<struct mixer_control *>(5);
+ inputs[1] = reinterpret_cast<struct mixer_control *>(6);
+
+ cras_alsa_mixer_list_inputs_outputs = inputs;
+ cras_alsa_mixer_list_inputs_outputs_length = ARRAY_SIZE(inputs);
+
+ cras_alsa_mixer_get_control_name_values[inputs[0]] = INTERNAL_MICROPHONE;
+ cras_alsa_mixer_get_control_name_values[inputs[1]] = "Mic";
+ auto_unplug_input_node_ret = 1;
+
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 1, fake_mixer, fake_config, fake_ucm,
+ CRAS_STREAM_INPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
+ EXPECT_EQ(1, cras_alsa_mixer_list_inputs_called);
+ EXPECT_EQ(2, cras_alsa_mixer_get_control_name_called);
+
+ // Assert that the the internal speaker is plugged and other nodes aren't.
+ ASSERT_NE(aio->base.nodes, (void *)NULL);
+ EXPECT_EQ(aio->base.nodes->plugged, 1);
+ ASSERT_NE(aio->base.nodes->next, (void *)NULL);
+ EXPECT_EQ(aio->base.nodes->next->plugged, 0);
+
+ // Plug headphone jack
+ cras_alsa_jack_get_name_ret_value = "Mic Jack";
+ is_utf8_string_ret_value = 1;
+ cras_alsa_jack_get_mixer_input_ret = inputs[1];
+ cras_alsa_jack_list_create_cb(jack, 1, cras_alsa_jack_list_create_cb_data);
+
+ // Assert internal speaker is auto unplugged
+ EXPECT_EQ(aio->base.nodes->plugged, 0);
+ EXPECT_EQ(aio->base.nodes->next->plugged, 1);
alsa_iodev_destroy((struct cras_iodev *)aio);
- free(fake_curve);
- fake_curve = NULL;
}
TEST(AlsaInitNode, SetNodeInitialState) {
@@ -699,6 +1511,7 @@ TEST(AlsaInitNode, SetNodeInitialState) {
ASSERT_EQ(0, node.plugged);
ASSERT_EQ(0, node.plugged_time.tv_sec);
ASSERT_EQ(CRAS_NODE_TYPE_UNKNOWN, node.type);
+ ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position);
memset(&node, 0, sizeof(node));
node.dev = &dev;
@@ -708,6 +1521,7 @@ TEST(AlsaInitNode, SetNodeInitialState) {
ASSERT_EQ(1, node.plugged);
ASSERT_GT(node.plugged_time.tv_sec, 0);
ASSERT_EQ(CRAS_NODE_TYPE_INTERNAL_SPEAKER, node.type);
+ ASSERT_EQ(NODE_POSITION_INTERNAL, node.position);
memset(&node, 0, sizeof(node));
node.dev = &dev;
@@ -716,7 +1530,8 @@ TEST(AlsaInitNode, SetNodeInitialState) {
set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL);
ASSERT_EQ(1, node.plugged);
ASSERT_GT(node.plugged_time.tv_sec, 0);
- ASSERT_EQ(CRAS_NODE_TYPE_INTERNAL_MIC, node.type);
+ ASSERT_EQ(CRAS_NODE_TYPE_MIC, node.type);
+ ASSERT_EQ(NODE_POSITION_INTERNAL, node.position);
memset(&node, 0, sizeof(node));
node.dev = &dev;
@@ -726,6 +1541,7 @@ TEST(AlsaInitNode, SetNodeInitialState) {
ASSERT_EQ(0, node.plugged);
ASSERT_EQ(0, node.plugged_time.tv_sec);
ASSERT_EQ(CRAS_NODE_TYPE_HDMI, node.type);
+ ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position);
memset(&node, 0, sizeof(node));
node.dev = &dev;
@@ -734,6 +1550,7 @@ TEST(AlsaInitNode, SetNodeInitialState) {
set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL);
ASSERT_EQ(0, node.plugged);
ASSERT_EQ(CRAS_NODE_TYPE_HDMI, node.type);
+ ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position);
memset(&node, 0, sizeof(node));
node.dev = &dev;
@@ -742,6 +1559,7 @@ TEST(AlsaInitNode, SetNodeInitialState) {
set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL);
ASSERT_EQ(0, node.plugged);
ASSERT_EQ(CRAS_NODE_TYPE_HDMI, node.type);
+ ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position);
memset(&node, 0, sizeof(node));
node.dev = &dev;
@@ -750,6 +1568,7 @@ TEST(AlsaInitNode, SetNodeInitialState) {
set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL);
ASSERT_EQ(0, node.plugged);
ASSERT_EQ(CRAS_NODE_TYPE_HDMI, node.type);
+ ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position);
memset(&node, 0, sizeof(node));
node.dev = &dev;
@@ -758,6 +1577,7 @@ TEST(AlsaInitNode, SetNodeInitialState) {
set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL);
ASSERT_EQ(0, node.plugged);
ASSERT_EQ(CRAS_NODE_TYPE_HEADPHONE, node.type);
+ ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position);
memset(&node, 0, sizeof(node));
node.dev = &dev;
@@ -766,6 +1586,7 @@ TEST(AlsaInitNode, SetNodeInitialState) {
set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL);
ASSERT_EQ(0, node.plugged);
ASSERT_EQ(CRAS_NODE_TYPE_HEADPHONE, node.type);
+ ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position);
memset(&node, 0, sizeof(node));
node.dev = &dev;
@@ -774,6 +1595,25 @@ TEST(AlsaInitNode, SetNodeInitialState) {
set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL);
ASSERT_EQ(0, node.plugged);
ASSERT_EQ(CRAS_NODE_TYPE_MIC, node.type);
+ ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position);
+
+ memset(&node, 0, sizeof(node));
+ node.dev = &dev;
+ strcpy(node.name, "Front Mic");
+ dev.direction = CRAS_STREAM_INPUT;
+ set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL);
+ ASSERT_EQ(1, node.plugged);
+ ASSERT_EQ(CRAS_NODE_TYPE_MIC, node.type);
+ ASSERT_EQ(NODE_POSITION_FRONT, node.position);
+
+ memset(&node, 0, sizeof(node));
+ node.dev = &dev;
+ strcpy(node.name, "Rear Mic");
+ dev.direction = CRAS_STREAM_INPUT;
+ set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL);
+ ASSERT_EQ(1, node.plugged);
+ ASSERT_EQ(CRAS_NODE_TYPE_MIC, node.type);
+ ASSERT_EQ(NODE_POSITION_REAR, node.position);
memset(&node, 0, sizeof(node));
node.dev = &dev;
@@ -782,6 +1622,7 @@ TEST(AlsaInitNode, SetNodeInitialState) {
set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL);
ASSERT_EQ(0, node.plugged);
ASSERT_EQ(CRAS_NODE_TYPE_MIC, node.type);
+ ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position);
memset(&node, 0, sizeof(node));
node.dev = &dev;
@@ -790,6 +1631,7 @@ TEST(AlsaInitNode, SetNodeInitialState) {
set_node_initial_state(&node, ALSA_CARD_TYPE_USB);
ASSERT_EQ(0, node.plugged);
ASSERT_EQ(CRAS_NODE_TYPE_USB, node.type);
+ ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position);
memset(&node, 0, sizeof(node));
node.dev = &dev;
@@ -798,6 +1640,7 @@ TEST(AlsaInitNode, SetNodeInitialState) {
set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL);
ASSERT_EQ(0, node.plugged);
ASSERT_EQ(CRAS_NODE_TYPE_MIC, node.type);
+ ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position);
memset(&node, 0, sizeof(node));
node.dev = &dev;
@@ -807,6 +1650,27 @@ TEST(AlsaInitNode, SetNodeInitialState) {
ASSERT_EQ(1, node.plugged);
ASSERT_GT(node.plugged_time.tv_sec, 0);
ASSERT_EQ(CRAS_NODE_TYPE_USB, node.type);
+ ASSERT_EQ(NODE_POSITION_EXTERNAL, node.position);
+
+ memset(&node, 0, sizeof(node));
+ node.dev = &dev;
+ strcpy(node.name, "Haptic");
+ dev.direction = CRAS_STREAM_OUTPUT;
+ set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL);
+ ASSERT_EQ(1, node.plugged);
+ ASSERT_GT(node.plugged_time.tv_sec, 0);
+ ASSERT_EQ(CRAS_NODE_TYPE_HAPTIC, node.type);
+ ASSERT_EQ(NODE_POSITION_INTERNAL, node.position);
+
+ memset(&node, 0, sizeof(node));
+ node.dev = &dev;
+ strcpy(node.name, "Rumbler");
+ dev.direction = CRAS_STREAM_OUTPUT;
+ set_node_initial_state(&node, ALSA_CARD_TYPE_INTERNAL);
+ ASSERT_EQ(1, node.plugged);
+ ASSERT_GT(node.plugged_time.tv_sec, 0);
+ ASSERT_EQ(CRAS_NODE_TYPE_HAPTIC, node.type);
+ ASSERT_EQ(NODE_POSITION_INTERNAL, node.position);
}
TEST(AlsaInitNode, SetNodeInitialStateDropInvalidUTF8NodeName) {
@@ -843,15 +1707,14 @@ TEST(AlsaInitNode, SetNodeInitialStateDropInvalidUTF8NodeName) {
TEST(AlsaIoInit, HDMIJackUpdateInvalidUTF8MonitorName) {
struct alsa_io *aio;
struct cras_alsa_mixer * const fake_mixer = (struct cras_alsa_mixer*)2;
- snd_use_case_mgr_t * const fake_ucm = (snd_use_case_mgr_t*)3;
+ struct cras_use_case_mgr * const fake_ucm = (struct cras_use_case_mgr*)3;
const struct cras_alsa_jack *jack = (struct cras_alsa_jack*)4;
ResetStubData();
- aio = (struct alsa_io *)alsa_iodev_create(0, test_card_name, 0, test_dev_name,
- NULL, ALSA_CARD_TYPE_INTERNAL, 0,
- fake_mixer, fake_ucm,
- CRAS_STREAM_OUTPUT, 0, 0);
- ASSERT_NE(aio, (void *)NULL);
+ aio = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL, ALSA_CARD_TYPE_INTERNAL, 0, fake_mixer, fake_config, fake_ucm,
+ CRAS_STREAM_OUTPUT);
+ ASSERT_EQ(0, alsa_iodev_legacy_complete_init((struct cras_iodev *)aio));
// Prepare the stub data such that the jack will be identified as an
// HDMI jack, and thus the callback creates an HDMI node.
@@ -864,7 +1727,7 @@ TEST(AlsaIoInit, HDMIJackUpdateInvalidUTF8MonitorName) {
// Add the jack node.
cras_alsa_jack_list_create_cb(jack, 1, cras_alsa_jack_list_create_cb_data);
- EXPECT_EQ(1, cras_alsa_jack_get_name_ret_called);
+ EXPECT_EQ(2, cras_alsa_jack_get_name_called);
ASSERT_EQ(CRAS_NODE_TYPE_HDMI, aio->base.nodes->next->type);
// The node name should be "HDMI".
ASSERT_STREQ("HDMI", aio->base.nodes->next->name);
@@ -877,43 +1740,95 @@ class AlsaVolumeMuteSuite : public testing::Test {
protected:
virtual void SetUp() {
ResetStubData();
- aio_output_ = (struct alsa_io *)alsa_iodev_create(
- 0, test_card_name, 0, test_dev_name, NULL,
- ALSA_CARD_TYPE_INTERNAL, 0,
- fake_mixer, NULL,
- CRAS_STREAM_OUTPUT, 0, 0);
+ output_control_ = reinterpret_cast<struct mixer_control *>(10);
+ cras_alsa_mixer_list_outputs_outputs = &output_control_;
+ cras_alsa_mixer_list_outputs_outputs_length = 1;
+ cras_alsa_mixer_get_control_name_values[output_control_] = "Speaker";
+ cras_alsa_mixer_list_outputs_outputs_length = 1;
+ aio_output_ = (struct alsa_io *)alsa_iodev_create_with_default_parameters(
+ 0, NULL,
+ ALSA_CARD_TYPE_INTERNAL, 1,
+ fake_mixer, fake_config, NULL,
+ CRAS_STREAM_OUTPUT);
+ alsa_iodev_legacy_complete_init((struct cras_iodev *)aio_output_);
+ EXPECT_EQ(2, cras_card_config_get_volume_curve_for_control_called);
+
+ struct cras_ionode *node;
+ int count = 0;
+ DL_FOREACH(aio_output_->base.nodes, node) {
+ printf("node %d \n", count);
+ }
aio_output_->base.direction = CRAS_STREAM_OUTPUT;
- aio_input_ = (struct alsa_io *)alsa_iodev_create(
- 0, test_card_name, 0, test_dev_name, NULL,
- ALSA_CARD_TYPE_INTERNAL, 0,
- fake_mixer, NULL,
- CRAS_STREAM_INPUT, 0, 0);
- aio_input_->base.direction = CRAS_STREAM_INPUT;
fmt_.frame_rate = 44100;
fmt_.num_channels = 2;
fmt_.format = SND_PCM_FORMAT_S16_LE;
- aio_input_->base.format = &fmt_;
aio_output_->base.format = &fmt_;
cras_alsa_get_avail_frames_ret = -1;
- fake_curve =
- static_cast<struct cras_volume_curve *>(calloc(1, sizeof(*fake_curve)));
- fake_curve->get_dBFS = fake_get_dBFS;
}
virtual void TearDown() {
alsa_iodev_destroy((struct cras_iodev *)aio_output_);
- alsa_iodev_destroy((struct cras_iodev *)aio_input_);
cras_alsa_get_avail_frames_ret = 0;
- free(fake_curve);
- fake_curve = NULL;
}
+ struct mixer_control *output_control_;
struct alsa_io *aio_output_;
- struct alsa_io *aio_input_;
struct cras_audio_format fmt_;
};
-TEST_F(AlsaVolumeMuteSuite, SetVolumeAndMute) {
+TEST_F(AlsaVolumeMuteSuite, GetDefaultVolumeCurve) {
+ int rc;
+ struct cras_audio_format *fmt;
+
+ fmt = (struct cras_audio_format *)malloc(sizeof(*fmt));
+ memcpy(fmt, &fmt_, sizeof(fmt_));
+ aio_output_->base.format = fmt;
+ aio_output_->handle = (snd_pcm_t *)0x24;
+
+ rc = aio_output_->base.open_dev(&aio_output_->base);
+ ASSERT_EQ(0, rc);
+ EXPECT_EQ(&default_curve, fake_get_dBFS_volume_curve_val);
+
+ aio_output_->base.set_volume(&aio_output_->base);
+ EXPECT_EQ(&default_curve, fake_get_dBFS_volume_curve_val);
+}
+
+TEST_F(AlsaVolumeMuteSuite, GetVolumeCurveFromNode)
+{
+ int rc;
+ struct cras_audio_format *fmt;
+ struct cras_alsa_jack *jack = (struct cras_alsa_jack*)4;
+ struct cras_ionode *node;
+ struct cras_volume_curve hp_curve = {
+ .get_dBFS = fake_get_dBFS,
+ };
+
+ fmt = (struct cras_audio_format *)malloc(sizeof(*fmt));
+ memcpy(fmt, &fmt_, sizeof(fmt_));
+ aio_output_->base.format = fmt;
+ aio_output_->handle = (snd_pcm_t *)0x24;
+
+ // Headphone jack plugged and has its own volume curve.
+ cras_alsa_jack_get_mixer_output_ret = NULL;
+ cras_alsa_jack_get_name_ret_value = "Headphone";
+ cras_card_config_get_volume_curve_vals["Headphone"] = &hp_curve;
+ cras_alsa_jack_list_create_cb(jack, 1, cras_alsa_jack_list_create_cb_data);
+ EXPECT_EQ(1, cras_alsa_jack_update_node_type_called);
+ EXPECT_EQ(3, cras_card_config_get_volume_curve_for_control_called);
+
+ // Switch to node 'Headphone'.
+ node = aio_output_->base.nodes->next;
+ aio_output_->base.active_node = node;
+
+ rc = aio_output_->base.open_dev(&aio_output_->base);
+ ASSERT_EQ(0, rc);
+ EXPECT_EQ(&hp_curve, fake_get_dBFS_volume_curve_val);
+
+ aio_output_->base.set_volume(&aio_output_->base);
+ EXPECT_EQ(&hp_curve, fake_get_dBFS_volume_curve_val);
+}
+
+TEST_F(AlsaVolumeMuteSuite, SetVolume) {
int rc;
struct cras_audio_format *fmt;
const size_t fake_system_volume = 55;
@@ -930,33 +1845,23 @@ TEST_F(AlsaVolumeMuteSuite, SetVolumeAndMute) {
ASSERT_EQ(0, rc);
EXPECT_EQ(1, alsa_mixer_set_dBFS_called);
EXPECT_EQ(fake_system_volume_dB, alsa_mixer_set_dBFS_value);
- EXPECT_EQ(1, alsa_mixer_set_mute_called);
- EXPECT_EQ(0, alsa_mixer_set_mute_value);
- alsa_mixer_set_mute_called = 0;
- alsa_mixer_set_mute_value = 0;
alsa_mixer_set_dBFS_called = 0;
alsa_mixer_set_dBFS_value = 0;
sys_get_volume_return_value = 50;
sys_get_volume_called = 0;
aio_output_->base.set_volume(&aio_output_->base);
EXPECT_EQ(1, sys_get_volume_called);
- EXPECT_EQ(1, alsa_mixer_set_mute_called);
- EXPECT_EQ(0, alsa_mixer_set_mute_value);
EXPECT_EQ(1, alsa_mixer_set_dBFS_called);
EXPECT_EQ(-5000, alsa_mixer_set_dBFS_value);
- EXPECT_EQ(NULL, alsa_mixer_set_dBFS_output);
+ EXPECT_EQ(output_control_, alsa_mixer_set_dBFS_output);
- alsa_mixer_set_mute_called = 0;
- alsa_mixer_set_mute_value = 0;
alsa_mixer_set_dBFS_called = 0;
alsa_mixer_set_dBFS_value = 0;
sys_get_volume_return_value = 0;
sys_get_volume_called = 0;
aio_output_->base.set_volume(&aio_output_->base);
EXPECT_EQ(1, sys_get_volume_called);
- EXPECT_EQ(1, alsa_mixer_set_mute_called);
- EXPECT_EQ(1, alsa_mixer_set_mute_value);
EXPECT_EQ(1, alsa_mixer_set_dBFS_called);
EXPECT_EQ(-10000, alsa_mixer_set_dBFS_value);
@@ -973,10 +1878,243 @@ TEST_F(AlsaVolumeMuteSuite, SetVolumeAndMute) {
free(fmt);
}
+TEST_F(AlsaVolumeMuteSuite, SetMute) {
+ int muted;
+
+ aio_output_->handle = (snd_pcm_t *)0x24;
+
+ // Test mute.
+ ResetStubData();
+ muted = 1;
+
+ sys_get_mute_return_value = muted;
+
+ aio_output_->base.set_mute(&aio_output_->base);
+
+ EXPECT_EQ(1, sys_get_mute_called);
+ EXPECT_EQ(1, alsa_mixer_set_mute_called);
+ EXPECT_EQ(muted, alsa_mixer_set_mute_value);
+ EXPECT_EQ(output_control_, alsa_mixer_set_mute_output);
+
+ // Test unmute.
+ ResetStubData();
+ muted = 0;
+
+ sys_get_mute_return_value = muted;
+
+ aio_output_->base.set_mute(&aio_output_->base);
+
+ EXPECT_EQ(1, sys_get_mute_called);
+ EXPECT_EQ(1, alsa_mixer_set_mute_called);
+ EXPECT_EQ(muted, alsa_mixer_set_mute_value);
+ EXPECT_EQ(output_control_, alsa_mixer_set_mute_output);
+}
+
+// Test free run.
+class AlsaFreeRunTestSuite: public testing::Test {
+ protected:
+ virtual void SetUp() {
+ ResetStubData();
+ memset(&aio, 0, sizeof(aio));
+ fmt_.format = SND_PCM_FORMAT_S16_LE;
+ fmt_.frame_rate = 48000;
+ fmt_.num_channels = 2;
+ aio.base.format = &fmt_;
+ aio.base.buffer_size = BUFFER_SIZE;
+ aio.base.min_cb_level = 240;
+ }
+
+ virtual void TearDown() {
+ }
+
+ struct alsa_io aio;
+ struct cras_audio_format fmt_;
+};
+
+TEST_F(AlsaFreeRunTestSuite, FillWholeBufferWithZeros) {
+ int rc;
+ int16_t *zeros;
+
+ cras_alsa_mmap_begin_buffer = (uint8_t *)calloc(
+ BUFFER_SIZE * 2 * 2,
+ sizeof(*cras_alsa_mmap_begin_buffer));
+ memset(cras_alsa_mmap_begin_buffer, 0xff,
+ sizeof(*cras_alsa_mmap_begin_buffer));
+
+ rc = fill_whole_buffer_with_zeros(&aio.base);
+
+ EXPECT_EQ(0, rc);
+ zeros = (int16_t *)calloc(BUFFER_SIZE * 2, sizeof(*zeros));
+ EXPECT_EQ(0, memcmp(zeros, cras_alsa_mmap_begin_buffer, BUFFER_SIZE * 2 * 2));
+
+ free(zeros);
+ free(cras_alsa_mmap_begin_buffer);
+}
+
+TEST_F(AlsaFreeRunTestSuite, EnterFreeRunAlreadyFreeRunning) {
+ int rc;
+
+ // Device is in free run state, no need to fill zeros or fill whole buffer.
+ aio.is_free_running = 1;
+
+ rc = no_stream(&aio.base, 1);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_alsa_mmap_get_whole_buffer_called);
+ EXPECT_EQ(0, cras_iodev_fill_odev_zeros_called);
+ EXPECT_EQ(0, cras_iodev_fill_odev_zeros_frames);
+}
+
+TEST_F(AlsaFreeRunTestSuite, EnterFreeRunNotDrainedYetNeedToFillZeros) {
+ int rc;
+
+ // Device is not in free run state. There are still valid samples to play.
+ // The number of valid samples is less than min_cb_level * 2.
+ // Need to fill zeros targeting min_cb_level * 2 = 480.
+ // The number of zeros to be filled is 480 - 200 = 280.
+ cras_iodev_frames_queued_ret = 200;
+ cras_iodev_buffer_avail_ret = BUFFER_SIZE - cras_iodev_frames_queued_ret;
+
+ rc = no_stream(&aio.base, 1);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_alsa_mmap_get_whole_buffer_called);
+ EXPECT_EQ(1, cras_iodev_fill_odev_zeros_called);
+ EXPECT_EQ(280, cras_iodev_fill_odev_zeros_frames);
+ EXPECT_EQ(280, aio.filled_zeros_for_draining);
+ EXPECT_EQ(0, aio.is_free_running);
+}
+
+TEST_F(AlsaFreeRunTestSuite, EnterFreeRunNotDrainedYetNoNeedToFillZeros) {
+ int rc;
+
+ // Device is not in free run state. There are still valid samples to play.
+ // The number of valid samples is more than min_cb_level * 2.
+ // No need to fill zeros.
+ cras_iodev_frames_queued_ret = 500;
+ cras_iodev_buffer_avail_ret = BUFFER_SIZE - cras_iodev_frames_queued_ret;
+
+ rc = no_stream(&aio.base, 1);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_alsa_mmap_get_whole_buffer_called);
+ EXPECT_EQ(0, cras_iodev_fill_odev_zeros_called);
+ EXPECT_EQ(0, aio.is_free_running);
+}
+
+TEST_F(AlsaFreeRunTestSuite, EnterFreeRunDrained) {
+ int rc;
+
+ // Device is not in free run state. There are still valid samples to play.
+ // The number of valid samples is less than filled zeros.
+ // Should enter free run state and fill whole buffer with zeros.
+ cras_iodev_frames_queued_ret = 40;
+ cras_iodev_buffer_avail_ret = BUFFER_SIZE - cras_iodev_frames_queued_ret;
+ aio.filled_zeros_for_draining = 100;
+
+ rc = no_stream(&aio.base, 1);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, cras_alsa_mmap_get_whole_buffer_called);
+ EXPECT_EQ(0, cras_iodev_fill_odev_zeros_called);
+ EXPECT_EQ(1, aio.is_free_running);
+}
+
+TEST_F(AlsaFreeRunTestSuite, EnterFreeRunNoSamples) {
+ int rc;
+
+ // Device is not in free run state. There is no sample to play.
+ // Should enter free run state and fill whole buffer with zeros.
+ cras_iodev_frames_queued_ret = 0;
+ cras_iodev_buffer_avail_ret = BUFFER_SIZE - cras_iodev_frames_queued_ret;
+
+ rc = no_stream(&aio.base, 1);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, cras_alsa_mmap_get_whole_buffer_called);
+ EXPECT_EQ(0, cras_iodev_fill_odev_zeros_called);
+ EXPECT_EQ(1, aio.is_free_running);
+}
+
+TEST_F(AlsaFreeRunTestSuite, OutputShouldWake) {
+
+ aio.is_free_running = 1;
+
+ EXPECT_EQ(0, output_should_wake(&aio.base));
+
+ aio.is_free_running = 0;
+ aio.base.state = CRAS_IODEV_STATE_NO_STREAM_RUN;
+ EXPECT_EQ(1, output_should_wake(&aio.base));
+
+ aio.base.state = CRAS_IODEV_STATE_NORMAL_RUN;
+ EXPECT_EQ(1, output_should_wake(&aio.base));
+
+ aio.base.state = CRAS_IODEV_STATE_OPEN;
+ EXPECT_EQ(0, output_should_wake(&aio.base));
+}
+
+TEST_F(AlsaFreeRunTestSuite, LeaveFreeRunNotInFreeRun) {
+ int rc;
+
+ rc = no_stream(&aio.base, 0);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_alsa_resume_appl_ptr_called);
+}
+
+TEST_F(AlsaFreeRunTestSuite, LeaveFreeRunInFreeRun) {
+ int rc;
+
+ aio.is_free_running = 1;
+ aio.filled_zeros_for_draining = 100;
+ aio.base.min_buffer_level = 512;
+
+ rc = no_stream(&aio.base, 0);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, cras_alsa_resume_appl_ptr_called);
+ EXPECT_EQ(aio.base.min_buffer_level + aio.base.min_cb_level,
+ cras_alsa_resume_appl_ptr_ahead);
+ EXPECT_EQ(0, aio.is_free_running);
+ EXPECT_EQ(0, aio.filled_zeros_for_draining);
+}
+
+// Reuse AlsaFreeRunTestSuite for output underrun handling because they are
+// similar.
+TEST_F(AlsaFreeRunTestSuite, OutputUnderrun) {
+ int rc;
+ int16_t *zeros;
+
+ cras_alsa_mmap_begin_buffer = (uint8_t *)calloc(
+ BUFFER_SIZE * 2 * 2,
+ sizeof(*cras_alsa_mmap_begin_buffer));
+ memset(cras_alsa_mmap_begin_buffer, 0xff,
+ sizeof(*cras_alsa_mmap_begin_buffer));
+
+ // Ask alsa_io to handle output underrun.
+ rc = alsa_output_underrun(&aio.base);
+ EXPECT_EQ(0, rc);
+
+ // mmap buffer should be filled with zeros.
+ zeros = (int16_t *)calloc(BUFFER_SIZE * 2, sizeof(*zeros));
+ EXPECT_EQ(0, memcmp(zeros, cras_alsa_mmap_begin_buffer, BUFFER_SIZE * 2 * 2));
+
+ // appl_ptr should be moved to min_buffer_level + min_cb_level ahead of
+ // hw_ptr.
+ EXPECT_EQ(1, cras_alsa_resume_appl_ptr_called);
+ EXPECT_EQ(aio.base.min_buffer_level + aio.base.min_cb_level,
+ cras_alsa_resume_appl_ptr_ahead);
+
+ free(zeros);
+ free(cras_alsa_mmap_begin_buffer);
+}
+
+
} // namespace
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
+ openlog(NULL, LOG_PERROR, LOG_USER);
return RUN_ALL_TESTS();
}
@@ -1003,6 +2141,22 @@ int cras_iodev_list_rm_input(struct cras_iodev *dev)
return 0;
}
+char *cras_iodev_list_get_hotword_models(cras_node_id_t node_id)
+{
+ return NULL;
+}
+
+int cras_iodev_list_set_hotword_model(cras_node_id_t node_id,
+ const char *model_name)
+{
+ return 0;
+}
+
+struct audio_thread *cras_iodev_list_get_audio_thread()
+{
+ return NULL;
+}
+
// From alsa helper.
int cras_alsa_set_channel_map(snd_pcm_t *handle,
struct cras_audio_format *fmt)
@@ -1055,18 +2209,24 @@ int cras_alsa_fill_properties(const char *dev,
return 0;
}
int cras_alsa_set_hwparams(snd_pcm_t *handle, struct cras_audio_format *format,
- snd_pcm_uframes_t *buffer_size)
+ snd_pcm_uframes_t *buffer_size, int period_wakeup,
+ unsigned int dma_period_time)
{
return 0;
}
-int cras_alsa_set_swparams(snd_pcm_t *handle)
+int cras_alsa_set_swparams(snd_pcm_t *handle, int *enable_htimestamp)
{
return 0;
}
int cras_alsa_get_avail_frames(snd_pcm_t *handle, snd_pcm_uframes_t buf_size,
- snd_pcm_uframes_t *used)
+ snd_pcm_uframes_t severe_underrun_frames,
+ const char* dev_name,
+ snd_pcm_uframes_t *used,
+ struct timespec *tstamp,
+ unsigned int *num_underruns)
{
*used = cras_alsa_get_avail_frames_avail;
+ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
return cras_alsa_get_avail_frames_ret;
}
int cras_alsa_get_delay_frames(snd_pcm_t *handle, snd_pcm_uframes_t buf_size,
@@ -1090,6 +2250,7 @@ int cras_alsa_mmap_commit(snd_pcm_t *handle, snd_pcm_uframes_t offset,
}
int cras_alsa_attempt_resume(snd_pcm_t *handle)
{
+ cras_alsa_attempt_resume_called++;
return 0;
}
@@ -1101,7 +2262,7 @@ int snd_pcm_format_physical_width(snd_pcm_format_t format)
snd_pcm_state_t snd_pcm_state(snd_pcm_t *handle)
{
- return SND_PCM_STATE_RUNNING;
+ return snd_pcm_state_ret;
}
const char *snd_strerror(int errnum)
@@ -1109,10 +2270,23 @@ const char *snd_strerror(int errnum)
return "Alsa Error in UT";
}
+struct mixer_control *cras_alsa_mixer_get_control_for_section(
+ struct cras_alsa_mixer *cras_mixer,
+ const struct ucm_section *section)
+{
+ cras_alsa_mixer_get_control_for_section_called++;
+ return cras_alsa_mixer_get_control_for_section_return_value;
+}
+
const char *cras_alsa_mixer_get_control_name(
const struct mixer_control *control)
{
- return "";
+ ControlNameMap::iterator it;
+ cras_alsa_mixer_get_control_name_called++;
+ it = cras_alsa_mixer_get_control_name_values.find(control);
+ if (it == cras_alsa_mixer_get_control_name_values.end())
+ return "";
+ return it->second.c_str();
}
// From system_state.
@@ -1147,6 +2321,8 @@ void cras_system_set_volume_limits(long min, long max)
void cras_system_set_capture_gain_limits(long min, long max)
{
+ cras_system_set_capture_gain_limits_set_value[0] = min;
+ cras_system_set_capture_gain_limits_set_value[1] = max;
sys_set_capture_gain_limits_called++;
}
@@ -1218,13 +2394,6 @@ void cras_alsa_mixer_list_inputs(struct cras_alsa_mixer *cras_mixer,
}
}
-struct cras_volume_curve *cras_alsa_mixer_create_volume_curve_for_name(
- const struct cras_alsa_mixer *cmix,
- const char *name)
-{
- return NULL;
-}
-
int cras_alsa_mixer_set_output_active_state(
struct mixer_control *output,
int active)
@@ -1235,13 +2404,6 @@ int cras_alsa_mixer_set_output_active_state(
return 0;
}
-const struct cras_volume_curve *cras_alsa_mixer_default_volume_curve(
- const struct cras_alsa_mixer *cras_mixer)
-{
- cras_alsa_mixer_default_volume_curve_called++;
- return fake_curve;
-}
-
void cras_volume_curve_destroy(struct cras_volume_curve *curve)
{
}
@@ -1251,7 +2413,7 @@ long cras_alsa_mixer_get_minimum_capture_gain(struct cras_alsa_mixer *cmix,
{
cras_alsa_mixer_get_minimum_capture_gain_called++;
cras_alsa_mixer_get_minimum_capture_gain_mixer_input = mixer_input;
- return 0;
+ return cras_alsa_mixer_get_minimum_capture_gain_ret_value;
}
long cras_alsa_mixer_get_maximum_capture_gain(struct cras_alsa_mixer *cmix,
@@ -1259,14 +2421,17 @@ long cras_alsa_mixer_get_maximum_capture_gain(struct cras_alsa_mixer *cmix,
{
cras_alsa_mixer_get_maximum_capture_gain_called++;
cras_alsa_mixer_get_maximum_capture_gain_mixer_input = mixer_input;
- return 0;
+ return cras_alsa_mixer_get_maximum_capture_gain_ret_value;
}
-struct cras_volume_curve *cras_alsa_mixer_get_output_volume_curve(
- const struct mixer_control *control)
+int cras_alsa_mixer_has_main_volume(const struct cras_alsa_mixer *cras_mixer)
{
- cras_alsa_mixer_get_output_volume_curve_called++;
- return cras_alsa_mixer_get_output_volume_curve_value;
+ return 1;
+}
+
+int cras_alsa_mixer_has_volume(const struct mixer_control *mixer_control)
+{
+ return 1;
}
// From cras_alsa_jack
@@ -1276,7 +2441,8 @@ struct cras_alsa_jack_list *cras_alsa_jack_list_create(
unsigned int device_index,
int check_gpio_jack,
struct cras_alsa_mixer *mixer,
- snd_use_case_mgr_t *ucm,
+ struct cras_use_case_mgr *ucm,
+ snd_hctl_t *hctl,
enum CRAS_STREAM_DIRECTION direction,
jack_state_change_callback *cb,
void *cb_data)
@@ -1287,11 +2453,34 @@ struct cras_alsa_jack_list *cras_alsa_jack_list_create(
return (struct cras_alsa_jack_list *)0xfee;
}
+int cras_alsa_jack_list_find_jacks_by_name_matching(
+ struct cras_alsa_jack_list *jack_list)
+{
+ cras_alsa_jack_list_find_jacks_by_name_matching_called++;
+ return 0;
+}
+
+int cras_alsa_jack_list_add_jack_for_section(
+ struct cras_alsa_jack_list *jack_list,
+ struct ucm_section *ucm_section,
+ struct cras_alsa_jack **result_jack)
+{
+ cras_alsa_jack_list_add_jack_for_section_called++;
+ if (result_jack)
+ *result_jack = cras_alsa_jack_list_add_jack_for_section_result_jack;
+ return 0;
+}
+
void cras_alsa_jack_list_destroy(struct cras_alsa_jack_list *jack_list)
{
cras_alsa_jack_list_destroy_called++;
}
+int cras_alsa_jack_list_has_hctl_jacks(struct cras_alsa_jack_list *jack_list)
+{
+ return cras_alsa_jack_list_has_hctl_jacks_return_val;
+}
+
void cras_alsa_jack_list_report(const struct cras_alsa_jack_list *jack_list)
{
}
@@ -1302,7 +2491,7 @@ void cras_alsa_jack_enable_ucm(const struct cras_alsa_jack *jack, int enable) {
const char *cras_alsa_jack_get_name(const struct cras_alsa_jack *jack)
{
- cras_alsa_jack_get_name_ret_called++;
+ cras_alsa_jack_get_name_called++;
return cras_alsa_jack_get_name_ret_value;
}
@@ -1312,7 +2501,8 @@ const char *cras_alsa_jack_get_dsp_name(const struct cras_alsa_jack *jack)
return jack ? cras_alsa_jack_get_dsp_name_value : NULL;
}
-const char *ucm_get_dsp_name_default(snd_use_case_mgr_t *mgr, int direction)
+const char *ucm_get_dsp_name_default(struct cras_use_case_mgr *mgr,
+ int direction)
{
ucm_get_dsp_name_default_called++;
if (ucm_get_dsp_name_default_value)
@@ -1324,49 +2514,122 @@ const char *ucm_get_dsp_name_default(snd_use_case_mgr_t *mgr, int direction)
struct mixer_control *cras_alsa_jack_get_mixer_output(
const struct cras_alsa_jack *jack)
{
- return NULL;
+ return cras_alsa_jack_get_mixer_output_ret;
}
struct mixer_control *cras_alsa_jack_get_mixer_input(
const struct cras_alsa_jack *jack)
{
- return NULL;
+ return cras_alsa_jack_get_mixer_input_ret;
}
-int ucm_set_enabled(snd_use_case_mgr_t *mgr, const char *dev, int enabled) {
+int ucm_set_enabled(
+ struct cras_use_case_mgr *mgr, const char *dev, int enabled) {
+ ucm_set_enabled_called++;
return 0;
}
-char *ucm_get_flag(snd_use_case_mgr_t *mgr, const char *flag_name) {
+char *ucm_get_flag(struct cras_use_case_mgr *mgr, const char *flag_name) {
+ char *ret = (char *)malloc(8);
+ if ((!strcmp(flag_name, "AutoUnplugInputNode") &&
+ auto_unplug_input_node_ret) ||
+ (!strcmp(flag_name, "AutoUnplugOutputNode") &&
+ auto_unplug_output_node_ret)) {
+ snprintf(ret, 8, "%s", "1");
+ return ret;
+ }
+
return NULL;
}
-char *ucm_get_mic_positions(snd_use_case_mgr_t *mgr) {
+char *ucm_get_mic_positions(struct cras_use_case_mgr *mgr) {
return NULL;
}
-int ucm_swap_mode_exists(snd_use_case_mgr_t *mgr)
+int ucm_swap_mode_exists(struct cras_use_case_mgr *mgr)
{
return ucm_swap_mode_exists_ret_value;
}
-int ucm_enable_swap_mode(snd_use_case_mgr_t *mgr, const char *node_name,
+int ucm_enable_swap_mode(struct cras_use_case_mgr *mgr, const char *node_name,
int enable)
{
ucm_enable_swap_mode_called++;
return ucm_enable_swap_mode_ret_value;
}
-unsigned int ucm_get_min_buffer_level(snd_use_case_mgr_t *mgr)
+unsigned int ucm_get_min_buffer_level(struct cras_use_case_mgr *mgr)
{
return 0;
}
-unsigned int ucm_get_disable_software_volume(snd_use_case_mgr_t *mgr)
+unsigned int ucm_get_enable_htimestamp_flag(struct cras_use_case_mgr *mgr)
+{
+ return ucm_get_enable_htimestamp_flag_ret;
+}
+
+unsigned int ucm_get_disable_software_volume(struct cras_use_case_mgr *mgr)
+{
+ return 0;
+}
+
+int ucm_get_max_software_gain(struct cras_use_case_mgr *mgr, const char *dev,
+ long *gain)
+{
+ ucm_get_max_software_gain_called++;
+ *gain = ucm_get_max_software_gain_value;
+ return ucm_get_max_software_gain_ret_value;
+}
+
+char *ucm_get_hotword_models(struct cras_use_case_mgr *mgr)
+{
+ return NULL;
+}
+
+int ucm_set_hotword_model(struct cras_use_case_mgr *mgr, const char *model)
{
return 0;
}
+unsigned int ucm_get_dma_period_for_dev(struct cras_use_case_mgr *mgr,
+ const char *dev)
+{
+ ucm_get_dma_period_for_dev_called++;
+ return ucm_get_dma_period_for_dev_ret;
+}
+
+int ucm_get_sample_rate_for_dev(struct cras_use_case_mgr *mgr, const char *dev,
+ enum CRAS_STREAM_DIRECTION direction)
+{
+ return -EINVAL;
+}
+
+int ucm_get_capture_chmap_for_dev(struct cras_use_case_mgr *mgr,
+ const char *dev,
+ int8_t *channel_layout)
+{
+ return -EINVAL;
+}
+
+struct cras_volume_curve *cras_volume_curve_create_default()
+{
+ return &default_curve;
+}
+
+struct cras_volume_curve *cras_card_config_get_volume_curve_for_control(
+ const struct cras_card_config *card_config,
+ const char *control_name)
+{
+ VolCurveMap::iterator it;
+ cras_card_config_get_volume_curve_for_control_called++;
+ if (!control_name)
+ return NULL;
+ it = cras_card_config_get_volume_curve_vals.find(control_name);
+ if (it == cras_card_config_get_volume_curve_vals.end())
+ return NULL;
+ return it->second;
+}
+
void cras_iodev_free_format(struct cras_iodev *iodev)
{
}
@@ -1375,6 +2638,8 @@ int cras_iodev_set_format(struct cras_iodev *iodev,
const struct cras_audio_format *fmt)
{
fake_format = (struct cras_audio_format *)calloc(1, sizeof(*fake_format));
+ // Copy the content of format from fmt into format of iodev.
+ memcpy(fake_format, fmt, sizeof(*fake_format));
iodev->format = fake_format;
return 0;
}
@@ -1386,6 +2651,8 @@ struct audio_thread *audio_thread_create() {
void audio_thread_destroy(audio_thread* thread) {
}
+
+
void cras_iodev_update_dsp(struct cras_iodev *iodev)
{
cras_iodev_update_dsp_called++;
@@ -1396,13 +2663,17 @@ int cras_iodev_set_node_attr(struct cras_ionode *ionode,
enum ionode_attr attr, int value)
{
cras_iodev_set_node_attr_called++;
+ cras_iodev_set_node_attr_ionode = ionode;
cras_iodev_set_node_attr_attr = attr;
cras_iodev_set_node_attr_value = value;
+ if (ionode && (attr == IONODE_ATTR_PLUGGED))
+ ionode->plugged = value;
return 0;
}
void cras_iodev_add_node(struct cras_iodev *iodev, struct cras_ionode *node)
{
+ cras_iodev_add_node_called++;
DL_APPEND(iodev->nodes, node);
}
@@ -1436,6 +2707,22 @@ void cras_alsa_jack_update_node_type(const struct cras_alsa_jack *jack,
cras_alsa_jack_update_node_type_called++;
}
+const char *cras_alsa_jack_get_ucm_device(const struct cras_alsa_jack *jack)
+{
+ return NULL;
+}
+
+int ucm_get_default_node_gain(struct cras_use_case_mgr *mgr, const char *dev,
+ long *gain)
+{
+ if (ucm_get_default_node_gain_values.find(dev) ==
+ ucm_get_default_node_gain_values.end())
+ return 1;
+
+ *gain = ucm_get_default_node_gain_values[dev];
+ return 0;
+}
+
void cras_iodev_init_audio_area(struct cras_iodev *iodev,
int num_channels) {
}
@@ -1443,6 +2730,29 @@ void cras_iodev_init_audio_area(struct cras_iodev *iodev,
void cras_iodev_free_audio_area(struct cras_iodev *iodev) {
}
+int cras_iodev_reset_rate_estimator(const struct cras_iodev *iodev)
+{
+ return 0;
+}
+
+int cras_iodev_frames_queued(struct cras_iodev *iodev, struct timespec *tstamp)
+{
+ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
+ return cras_iodev_frames_queued_ret;
+}
+
+int cras_iodev_buffer_avail(struct cras_iodev *iodev, unsigned hw_level)
+{
+ return cras_iodev_buffer_avail_ret;
+}
+
+int cras_iodev_fill_odev_zeros(struct cras_iodev *odev, unsigned int frames)
+{
+ cras_iodev_fill_odev_zeros_called++;
+ cras_iodev_fill_odev_zeros_frames = frames;
+ return 0;
+}
+
void cras_audio_area_config_buf_pointers(struct cras_audio_area *area,
const struct cras_audio_format *fmt,
uint8_t *base_buffer)
@@ -1457,9 +2767,51 @@ void audio_thread_rm_callback(int fd)
{
}
+int audio_thread_rm_callback_sync(struct audio_thread *thread, int fd) {
+ return 0;
+}
+
int is_utf8_string(const char* string)
{
return is_utf8_string_ret_value;
}
+int cras_alsa_mmap_get_whole_buffer(snd_pcm_t *handle, uint8_t **dst,
+ unsigned int *underruns)
+{
+ snd_pcm_uframes_t offset, frames;
+
+ cras_alsa_mmap_get_whole_buffer_called++;
+ return cras_alsa_mmap_begin(handle, 0, dst, &offset, &frames, underruns);
+}
+
+int cras_alsa_resume_appl_ptr(snd_pcm_t *handle, snd_pcm_uframes_t ahead)
+{
+ cras_alsa_resume_appl_ptr_called++;
+ cras_alsa_resume_appl_ptr_ahead = ahead;
+ return 0;
+}
+
+int cras_iodev_default_no_stream_playback(struct cras_iodev *odev, int enable)
+{
+ return 0;
+}
+
+enum CRAS_IODEV_STATE cras_iodev_state(const struct cras_iodev *iodev)
+{
+ return iodev->state;
+}
+
+int cras_iodev_dsp_set_swap_mode_for_node(struct cras_iodev *iodev,
+ struct cras_ionode *node,
+ int enable)
+{
+ cras_iodev_dsp_set_swap_mode_for_node_called++;
+ return 0;
+}
+
+struct cras_ramp* cras_ramp_create() {
+ return (struct cras_ramp*)0x1;
+}
+
}
diff --git a/cras/src/tests/alsa_jack_unittest.cc b/cras/src/tests/alsa_jack_unittest.cc
index 2bfe6f6c..e188f87f 100644
--- a/cras/src/tests/alsa_jack_unittest.cc
+++ b/cras/src/tests/alsa_jack_unittest.cc
@@ -4,14 +4,19 @@
#include <deque>
#include <linux/input.h>
+#include <map>
#include <poll.h>
#include <stdio.h>
#include <sys/param.h>
#include <gtest/gtest.h>
#include <string>
+#include <syslog.h>
+#include <vector>
extern "C" {
#include "cras_alsa_jack.h"
+#include "cras_alsa_ucm_section.h"
+#include "cras_gpio_jack.h"
#include "cras_tm.h"
#include "cras_types.h"
#include "cras_util.h"
@@ -27,13 +32,7 @@ namespace {
#define LONG(x) ((x) / BITS_PER_LONG)
#define IS_BIT_SET(bit, array) !!((array[LONG(bit)]) & (1UL << OFF(bit)))
-static size_t snd_hctl_open_called;
-static int snd_hctl_open_return_value;
-static snd_hctl_t *snd_hctl_open_pointer_val;
-static size_t snd_hctl_load_called;
-static int snd_hctl_load_return_value;
static int fake_jack_cb_plugged;
-static int snd_hctl_close_called;
static void *fake_jack_cb_data;
static size_t fake_jack_cb_called;
unsigned int snd_hctl_elem_get_device_return_val;
@@ -47,14 +46,13 @@ static size_t snd_hctl_elem_get_name_called;
static size_t snd_hctl_elem_set_callback_called;
static snd_hctl_elem_t *snd_hctl_elem_set_callback_obj;
static snd_hctl_elem_callback_t snd_hctl_elem_set_callback_value;
-static struct pollfd *snd_hctl_poll_descriptors_fds;
-static size_t snd_hctl_poll_descriptors_num_fds;
-static size_t snd_hctl_poll_descriptors_called;
+static size_t snd_hctl_find_elem_called;
+static std::vector<snd_hctl_elem_t *> snd_hctl_find_elem_return_vals;
+static std::map<std::string, size_t> snd_ctl_elem_id_set_name_map;
static size_t cras_system_add_select_fd_called;
static std::vector<int> cras_system_add_select_fd_values;
static size_t cras_system_rm_select_fd_called;
static std::vector<int> cras_system_rm_select_fd_values;
-static size_t snd_hctl_handle_events_called;
static size_t snd_hctl_elem_set_callback_private_called;
static void *snd_hctl_elem_set_callback_private_value;
static size_t snd_hctl_elem_get_hctl_called;
@@ -62,20 +60,22 @@ static snd_hctl_t *snd_hctl_elem_get_hctl_return_value;
static size_t snd_ctl_elem_value_get_boolean_called;
static int snd_ctl_elem_value_get_boolean_return_value;
static void *fake_jack_cb_arg;
-static size_t snd_hctl_nonblock_called;
static struct cras_alsa_mixer *fake_mixer;
static size_t cras_alsa_mixer_get_output_matching_name_called;
static size_t cras_alsa_mixer_get_input_matching_name_called;
-static struct mixer_output_control *
+static size_t cras_alsa_mixer_get_control_for_section_called;
+static struct mixer_control *
cras_alsa_mixer_get_output_matching_name_return_value;
-struct mixer_volume_control *
+static struct mixer_control *
cras_alsa_mixer_get_input_matching_name_return_value;
-static size_t gpio_get_switch_names_called;
-static size_t gpio_get_switch_names_count;
+static struct mixer_control *
+ cras_alsa_mixer_get_control_for_section_return_value;
+static size_t gpio_switch_list_for_each_called;
+static std::vector<std::string> gpio_switch_list_for_each_dev_paths;
+static std::vector<std::string> gpio_switch_list_for_each_dev_names;
static size_t gpio_switch_open_called;
static size_t gpio_switch_eviocgsw_called;
static size_t gpio_switch_eviocgbit_called;
-static size_t sys_input_get_device_name_called;
static unsigned ucm_get_dev_for_jack_called;
static unsigned ucm_get_cap_control_called;
static char *ucm_get_cap_control_value;
@@ -87,20 +87,17 @@ static const char *edid_file_ret;
static size_t ucm_get_dsp_name_called;
static unsigned ucm_get_override_type_name_called;
static char *ucm_get_device_name_for_dev_value;
+static snd_hctl_t *fake_hctl = (snd_hctl_t *)2;
static void ResetStubData() {
- gpio_get_switch_names_called = 0;
- gpio_get_switch_names_count = 0;
+ gpio_switch_list_for_each_called = 0;
+ gpio_switch_list_for_each_dev_paths.clear();
+ gpio_switch_list_for_each_dev_paths.push_back("/dev/input/event3");
+ gpio_switch_list_for_each_dev_paths.push_back("/dev/input/event2");
+ gpio_switch_list_for_each_dev_names.clear();
gpio_switch_open_called = 0;
gpio_switch_eviocgsw_called = 0;
gpio_switch_eviocgbit_called = 0;
- sys_input_get_device_name_called = 0;
- snd_hctl_open_called = 0;
- snd_hctl_open_return_value = 0;
- snd_hctl_open_pointer_val = reinterpret_cast<snd_hctl_t *>(0x4323);
- snd_hctl_load_called = 0;
- snd_hctl_load_return_value = 0;
- snd_hctl_close_called = 0;
snd_hctl_elem_get_device_return_val = 0;
snd_hctl_elem_get_device_called = 0;
snd_hctl_first_elem_called = 0;
@@ -110,26 +107,30 @@ static void ResetStubData() {
snd_hctl_elem_next_ret_vals_poped.clear();
snd_hctl_elem_get_name_called = 0;
snd_hctl_elem_set_callback_called = 0;
- snd_hctl_poll_descriptors_num_fds = 0;
- snd_hctl_poll_descriptors_called = 0;
+ snd_hctl_elem_set_callback_obj = NULL;
+ snd_hctl_elem_set_callback_value = NULL;
+ snd_hctl_find_elem_called = 0;
+ snd_hctl_find_elem_return_vals.clear();
+ snd_ctl_elem_id_set_name_map.clear();
cras_system_add_select_fd_called = 0;
cras_system_add_select_fd_values.clear();
cras_system_rm_select_fd_called = 0;
cras_system_rm_select_fd_values.clear();
- snd_hctl_handle_events_called = 0;
snd_hctl_elem_set_callback_private_called = 0;
snd_hctl_elem_get_hctl_called = 0;
snd_ctl_elem_value_get_boolean_called = 0;
fake_jack_cb_called = 0;
fake_jack_cb_plugged = 0;
fake_jack_cb_arg = reinterpret_cast<void *>(0x987);
- snd_hctl_nonblock_called = 0;
fake_mixer = reinterpret_cast<struct cras_alsa_mixer *>(0x789);
cras_alsa_mixer_get_output_matching_name_called = 0;
cras_alsa_mixer_get_input_matching_name_called = 0;
+ cras_alsa_mixer_get_control_for_section_called = 0;
cras_alsa_mixer_get_output_matching_name_return_value =
- reinterpret_cast<struct mixer_output_control *>(0x456);
+ reinterpret_cast<struct mixer_control *>(0x456);
cras_alsa_mixer_get_input_matching_name_return_value = NULL;
+ cras_alsa_mixer_get_control_for_section_return_value =
+ reinterpret_cast<struct mixer_control *>(0x456);
ucm_get_dev_for_jack_called = 0;
ucm_get_cap_control_called = 0;
ucm_get_cap_control_value = NULL;
@@ -157,54 +158,23 @@ static void fake_jack_cb(const struct cras_alsa_jack *jack,
ucm_set_enabled_value);
}
-TEST(AlsaJacks, CreateFailInvalidParams) {
- EXPECT_EQ(NULL, cras_alsa_jack_list_create(32, "c1", 0, 1,
- fake_mixer,
- NULL,
- CRAS_STREAM_OUTPUT,
- fake_jack_cb,
- fake_jack_cb_arg));
- EXPECT_EQ(0, snd_hctl_open_called);
- EXPECT_EQ(NULL, cras_alsa_jack_list_create(0, "c1", 32, 1,
- fake_mixer,
- NULL,
- CRAS_STREAM_OUTPUT,
- fake_jack_cb,
- fake_jack_cb_arg));
- EXPECT_EQ(0, snd_hctl_open_called);
-}
-
-TEST(AlsaJacks, CreateFailOpen) {
- ResetStubData();
- snd_hctl_open_return_value = -1;
- snd_hctl_open_pointer_val = NULL;
- EXPECT_EQ(NULL, cras_alsa_jack_list_create(0, "c1", 0, 1,
- fake_mixer,
- NULL,
- CRAS_STREAM_OUTPUT,
- fake_jack_cb,
- fake_jack_cb_arg));
- EXPECT_EQ(1, snd_hctl_open_called);
-}
-
-TEST(AlsaJacks, CreateFailLoad) {
+TEST(AlsaJacks, CreateNullHctl) {
+ struct cras_alsa_jack_list *jack_list;
ResetStubData();
- snd_hctl_load_return_value = -1;
- gpio_get_switch_names_count = ~0;
- EXPECT_EQ(NULL, cras_alsa_jack_list_create(0, "c1", 0, 1,
- fake_mixer,
- NULL,
- CRAS_STREAM_OUTPUT,
- fake_jack_cb,
- fake_jack_cb_arg));
- EXPECT_EQ(0, gpio_get_switch_names_called);
+ jack_list = cras_alsa_jack_list_create(0, "c1", 0, 1,
+ fake_mixer,
+ NULL, NULL,
+ CRAS_STREAM_OUTPUT,
+ fake_jack_cb,
+ fake_jack_cb_arg);
+ ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), jack_list);
+ EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
+ EXPECT_EQ(1, gpio_switch_list_for_each_called);
EXPECT_EQ(0, gpio_switch_open_called);
EXPECT_EQ(0, gpio_switch_eviocgsw_called);
EXPECT_EQ(0, gpio_switch_eviocgbit_called);
- EXPECT_EQ(0, sys_input_get_device_name_called);
- EXPECT_EQ(1, snd_hctl_open_called);
- EXPECT_EQ(1, snd_hctl_load_called);
- EXPECT_EQ(1, snd_hctl_close_called);
+
+ cras_alsa_jack_list_destroy(jack_list);
}
TEST(AlsaJacks, CreateNoElements) {
@@ -212,33 +182,29 @@ TEST(AlsaJacks, CreateNoElements) {
ResetStubData();
snd_hctl_first_elem_return_val = NULL;
- gpio_get_switch_names_count = 0;
jack_list = cras_alsa_jack_list_create(0, "c1", 0, 1,
fake_mixer,
- NULL,
+ NULL, fake_hctl,
CRAS_STREAM_OUTPUT,
fake_jack_cb,
fake_jack_cb_arg);
ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), jack_list);
- EXPECT_EQ(1, gpio_get_switch_names_called);
+ EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
+ EXPECT_EQ(1, gpio_switch_list_for_each_called);
EXPECT_EQ(0, gpio_switch_open_called);
EXPECT_EQ(0, gpio_switch_eviocgsw_called);
EXPECT_EQ(0, gpio_switch_eviocgbit_called);
- EXPECT_EQ(0, sys_input_get_device_name_called);
- EXPECT_EQ(1, snd_hctl_open_called);
- EXPECT_EQ(1, snd_hctl_load_called);
EXPECT_EQ(1, snd_hctl_first_elem_called);
EXPECT_EQ(0, snd_hctl_elem_next_called);
cras_alsa_jack_list_destroy(jack_list);
- EXPECT_EQ(1, snd_hctl_close_called);
}
static struct cras_alsa_jack_list *run_test_with_elem_list(
CRAS_STREAM_DIRECTION direction,
std::string *elems,
unsigned int device_index,
- snd_use_case_mgr_t *ucm,
+ struct cras_use_case_mgr *ucm,
size_t nelems,
size_t nhdmi_jacks,
size_t njacks) {
@@ -255,16 +221,15 @@ static struct cras_alsa_jack_list *run_test_with_elem_list(
device_index,
1,
fake_mixer,
- ucm,
+ ucm, fake_hctl,
direction,
fake_jack_cb,
fake_jack_cb_arg);
if (jack_list == NULL)
return jack_list;
+ 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, snd_hctl_open_called);
- EXPECT_EQ(1, snd_hctl_load_called);
EXPECT_EQ(1 + nhdmi_jacks, snd_hctl_first_elem_called);
EXPECT_EQ(njacks, snd_hctl_elem_set_callback_called);
@@ -282,6 +247,53 @@ static struct cras_alsa_jack_list *run_test_with_elem_list(
return jack_list;
}
+static struct cras_alsa_jack_list *run_test_with_section(
+ CRAS_STREAM_DIRECTION direction,
+ std::string *elems,
+ size_t nelems,
+ unsigned int device_index,
+ struct cras_use_case_mgr *ucm,
+ struct ucm_section *ucm_section,
+ int add_jack_rc,
+ size_t njacks) {
+ struct cras_alsa_jack_list *jack_list;
+ struct cras_alsa_jack *jack;
+
+ for (size_t i = 0; i < nelems; i++) {
+ snd_ctl_elem_id_set_name_map[elems[i]] = i;
+ snd_hctl_find_elem_return_vals.push_back(
+ reinterpret_cast<snd_hctl_elem_t*>(&elems[i]));
+ }
+
+ jack_list = cras_alsa_jack_list_create(0,
+ "card_name",
+ device_index,
+ 1,
+ fake_mixer,
+ ucm, fake_hctl,
+ direction,
+ fake_jack_cb,
+ fake_jack_cb_arg);
+ if (jack_list == NULL)
+ return jack_list;
+ EXPECT_EQ(add_jack_rc,
+ cras_alsa_jack_list_add_jack_for_section(jack_list, ucm_section, &jack));
+ if (add_jack_rc == 0) {
+ EXPECT_EQ(njacks, ucm_get_dsp_name_called);
+ EXPECT_NE(jack, reinterpret_cast<struct cras_alsa_jack *>(NULL));
+ } else {
+ EXPECT_EQ(jack, reinterpret_cast<struct cras_alsa_jack *>(NULL));
+ }
+ if (add_jack_rc != 0 || njacks != ucm_get_dsp_name_called) {
+ cras_alsa_jack_list_destroy(jack_list);
+ return NULL;
+ }
+ EXPECT_EQ(njacks, snd_hctl_elem_set_callback_called);
+ EXPECT_EQ(njacks, cras_alsa_mixer_get_control_for_section_called);
+
+ return jack_list;
+}
+
TEST(AlsaJacks, ReportNull) {
cras_alsa_jack_list_report(NULL);
}
@@ -298,47 +310,48 @@ TEST(AlsaJacks, CreateNoJacks) {
jack_list = run_test_with_elem_list(CRAS_STREAM_OUTPUT,
elem_names,
0,
- NULL,
+ NULL,
ARRAY_SIZE(elem_names),
0,
0);
ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), jack_list);
cras_alsa_jack_list_destroy(jack_list);
- EXPECT_EQ(1, snd_hctl_close_called);
+ EXPECT_EQ(0, cras_system_rm_select_fd_called);
}
TEST(AlsaJacks, CreateGPIOHp) {
struct cras_alsa_jack_list *jack_list;
ResetStubData();
- gpio_get_switch_names_count = ~0;
+ gpio_switch_list_for_each_dev_names.push_back("some-other-device");
+ gpio_switch_list_for_each_dev_names.push_back("c1 Headphone Jack");
eviocbit_ret[LONG(SW_HEADPHONE_INSERT)] |= 1 << OFF(SW_HEADPHONE_INSERT);
gpio_switch_eviocgbit_fd = 2;
snd_hctl_first_elem_return_val = NULL;
jack_list = cras_alsa_jack_list_create(0, "c1", 0, 1,
fake_mixer,
- NULL,
+ NULL, fake_hctl,
CRAS_STREAM_OUTPUT,
fake_jack_cb,
fake_jack_cb_arg);
ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), jack_list);
-
+ EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
cras_alsa_jack_list_destroy(jack_list);
- EXPECT_EQ(1, gpio_get_switch_names_called);
+ EXPECT_EQ(1, gpio_switch_list_for_each_called);
EXPECT_GT(gpio_switch_open_called, 1);
EXPECT_EQ(1, gpio_switch_eviocgsw_called);
EXPECT_GT(gpio_switch_eviocgbit_called, 1);
- EXPECT_GT(sys_input_get_device_name_called, 1);
EXPECT_EQ(1, cras_system_add_select_fd_called);
- EXPECT_EQ(1, snd_hctl_close_called);
+ EXPECT_EQ(1, cras_system_rm_select_fd_called);
}
TEST(AlsaJacks, CreateGPIOMic) {
struct cras_alsa_jack_list *jack_list;
ResetStubData();
ucm_get_dev_for_jack_return = true;
- gpio_get_switch_names_count = ~0;
+ gpio_switch_list_for_each_dev_names.push_back("c1 Mic Jack");
+ gpio_switch_list_for_each_dev_names.push_back("c1 Headphone Jack");
eviocbit_ret[LONG(SW_MICROPHONE_INSERT)] |= 1 << OFF(SW_MICROPHONE_INSERT);
gpio_switch_eviocgbit_fd = 3;
snd_hctl_first_elem_return_val = NULL;
@@ -346,7 +359,7 @@ TEST(AlsaJacks, CreateGPIOMic) {
// Freed in destroy.
cras_alsa_mixer_get_input_matching_name_return_value =
- reinterpret_cast<struct mixer_volume_control *>(malloc(1));
+ reinterpret_cast<struct mixer_control *>(malloc(1));
jack_list = cras_alsa_jack_list_create(
0,
@@ -354,11 +367,13 @@ TEST(AlsaJacks, CreateGPIOMic) {
0,
1,
fake_mixer,
- reinterpret_cast<snd_use_case_mgr_t*>(0x55),
+ reinterpret_cast<struct cras_use_case_mgr *>(0x55),
+ fake_hctl,
CRAS_STREAM_INPUT,
fake_jack_cb,
fake_jack_cb_arg);
ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), jack_list);
+ EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
EXPECT_EQ(ucm_get_cap_control_called, 1);
EXPECT_EQ(cras_alsa_mixer_get_input_matching_name_called, 1);
cras_alsa_jack_list_destroy(jack_list);
@@ -368,17 +383,19 @@ TEST(AlsaJacks, CreateGPIOHdmi) {
struct cras_alsa_jack_list *jack_list;
ResetStubData();
- gpio_get_switch_names_count = ~0;
+ gpio_switch_list_for_each_dev_names.push_back("c1 HDMI Jack");
+ gpio_switch_list_for_each_dev_names.push_back("c1 Mic Jack");
eviocbit_ret[LONG(SW_LINEOUT_INSERT)] |= 1 << OFF(SW_LINEOUT_INSERT);
gpio_switch_eviocgbit_fd = 3;
snd_hctl_first_elem_return_val = NULL;
jack_list = cras_alsa_jack_list_create(0, "c1", 0, 1,
fake_mixer,
- NULL,
+ NULL, fake_hctl,
CRAS_STREAM_OUTPUT,
fake_jack_cb,
fake_jack_cb_arg);
ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), jack_list);
+ EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
EXPECT_EQ(1, gpio_switch_eviocgsw_called);
fake_jack_cb_called = 0;
@@ -387,38 +404,43 @@ TEST(AlsaJacks, CreateGPIOHdmi) {
EXPECT_EQ(1, fake_jack_cb_called);
cras_alsa_jack_list_destroy(jack_list);
- EXPECT_EQ(1, gpio_get_switch_names_called);
+ EXPECT_EQ(1, gpio_switch_list_for_each_called);
EXPECT_GT(gpio_switch_open_called, 1);
EXPECT_GT(gpio_switch_eviocgbit_called, 1);
- EXPECT_GT(sys_input_get_device_name_called, 1);
EXPECT_EQ(1, cras_system_add_select_fd_called);
- EXPECT_EQ(1, snd_hctl_close_called);
+ EXPECT_EQ(1, cras_system_rm_select_fd_called);
}
void run_gpio_jack_test(
int device_index,
int is_first_device,
enum CRAS_STREAM_DIRECTION direction,
- int should_create_jack)
+ int should_create_jack,
+ const char* jack_name)
{
struct cras_alsa_jack_list *jack_list;
- snd_use_case_mgr_t *ucm = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ struct cras_use_case_mgr *ucm =
+ reinterpret_cast<struct cras_use_case_mgr *>(0x55);
- gpio_get_switch_names_count = ~0;
+ gpio_switch_list_for_each_dev_names.push_back("some-other-device one");
gpio_switch_eviocgbit_fd = 2;
- if (direction == CRAS_STREAM_OUTPUT)
+ if (direction == CRAS_STREAM_OUTPUT) {
eviocbit_ret[LONG(SW_HEADPHONE_INSERT)] |= 1 << OFF(SW_HEADPHONE_INSERT);
- else
+ } else {
eviocbit_ret[LONG(SW_MICROPHONE_INSERT)] |= 1 << OFF(SW_MICROPHONE_INSERT);
+ }
+ gpio_switch_list_for_each_dev_names.push_back(jack_name);
snd_hctl_first_elem_return_val = NULL;
- jack_list = cras_alsa_jack_list_create(0, "c1", device_index, is_first_device,
+ jack_list = cras_alsa_jack_list_create(0, "c1", device_index,
+ is_first_device,
fake_mixer,
- ucm,
+ ucm, fake_hctl,
direction,
fake_jack_cb,
fake_jack_cb_arg);
ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), jack_list);
+ EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
cras_alsa_jack_list_report(jack_list);
EXPECT_EQ(should_create_jack, fake_jack_cb_plugged);
@@ -440,7 +462,8 @@ TEST(AlsaJacks, CreateGPIOHpUCMPlaybackPCMMatched) {
ucm_get_device_name_for_dev_value = strdup("hw:c1,1");
run_gpio_jack_test(
- device_index, is_first_device, direction, should_create_jack);
+ device_index, is_first_device, direction, should_create_jack,
+ "c1 Headset Jack");
}
TEST(AlsaJacks, CreateGPIOHpUCMCapturePCMMatched) {
@@ -456,7 +479,8 @@ TEST(AlsaJacks, CreateGPIOHpUCMCapturePCMMatched) {
ucm_get_device_name_for_dev_value = strdup("hw:c1,1");
run_gpio_jack_test(
- device_index, is_first_device, direction, should_create_jack);
+ device_index, is_first_device, direction, should_create_jack,
+ "c1 Mic Jack");
}
TEST(AlsaJacks, CreateGPIOHpUCMPlaybackPCMNotMatched) {
@@ -472,7 +496,8 @@ TEST(AlsaJacks, CreateGPIOHpUCMPlaybackPCMNotMatched) {
ucm_get_device_name_for_dev_value = strdup("hw:c1,2");
run_gpio_jack_test(
- device_index, is_first_device, direction, should_create_jack);
+ device_index, is_first_device, direction, should_create_jack,
+ "c1 Headset Jack");
}
TEST(AlsaJacks, CreateGPIOHpUCMPlaybackPCMNotSpecifiedFirstDevice) {
@@ -488,7 +513,8 @@ TEST(AlsaJacks, CreateGPIOHpUCMPlaybackPCMNotSpecifiedFirstDevice) {
ucm_get_device_name_for_dev_value = NULL;
run_gpio_jack_test(
- device_index, is_first_device, direction, should_create_jack);
+ device_index, is_first_device, direction, should_create_jack,
+ "c1 Headset Jack");
}
TEST(AlsaJacks, CreateGPIOHpUCMPlaybackPCMNotSpecifiedSecondDevice) {
@@ -504,7 +530,8 @@ TEST(AlsaJacks, CreateGPIOHpUCMPlaybackPCMNotSpecifiedSecondDevice) {
ucm_get_device_name_for_dev_value = NULL;
run_gpio_jack_test(
- device_index, is_first_device, direction, should_create_jack);
+ device_index, is_first_device, direction, should_create_jack,
+ "c1 Headset Jack");
}
TEST(AlsaJacks, CreateGPIOHpNoUCMFirstDevice) {
@@ -520,7 +547,8 @@ TEST(AlsaJacks, CreateGPIOHpNoUCMFirstDevice) {
ucm_get_device_name_for_dev_value = NULL;
run_gpio_jack_test(
- device_index, is_first_device, direction, should_create_jack);
+ device_index, is_first_device, direction, should_create_jack,
+ "c1 Headset Jack");
}
TEST(AlsaJacks, CreateGPIOHpNoUCMSecondDevice) {
@@ -536,7 +564,44 @@ TEST(AlsaJacks, CreateGPIOHpNoUCMSecondDevice) {
ucm_get_device_name_for_dev_value = NULL;
run_gpio_jack_test(
- device_index, is_first_device, direction, should_create_jack);
+ device_index, is_first_device, direction, should_create_jack,
+ "c1 Headset Jack");
+}
+
+TEST(AlsaJacks, CreateGPIOMicNoUCMFirstDeviceMicJack) {
+ int device_index = 1;
+ int is_first_device = 1;
+ enum CRAS_STREAM_DIRECTION direction = CRAS_STREAM_INPUT;
+ int should_create_jack = 1;
+
+ ResetStubData();
+
+ // No UCM for this jack, create jack for the first device.
+ ucm_get_dev_for_jack_return = false;
+ ucm_get_device_name_for_dev_value = NULL;
+
+ // Mic Jack is a valid name for microphone jack.
+ run_gpio_jack_test(
+ device_index, is_first_device, direction, should_create_jack,
+ "c1 Mic Jack");
+}
+
+TEST(AlsaJacks, CreateGPIOMicNoUCMFirstDeviceHeadsetJack) {
+ int device_index = 1;
+ int is_first_device = 1;
+ enum CRAS_STREAM_DIRECTION direction = CRAS_STREAM_INPUT;
+ int should_create_jack = 1;
+
+ ResetStubData();
+
+ // No UCM for this jack, create jack for the first device.
+ ucm_get_dev_for_jack_return = false;
+ ucm_get_device_name_for_dev_value = NULL;
+
+ // Headset Jack is a valid name for microphone jack.
+ run_gpio_jack_test(
+ device_index, is_first_device, direction, should_create_jack,
+ "c1 Headset Jack");
}
TEST(AlsaJacks, GPIOHdmiWithEdid) {
@@ -545,7 +610,7 @@ TEST(AlsaJacks, GPIOHdmiWithEdid) {
ResetStubData();
ucm_get_dev_for_jack_return = 1;
edid_file_ret = static_cast<char*>(calloc(1, 1)); // Freed in destroy.
- gpio_get_switch_names_count = ~0;
+ gpio_switch_list_for_each_dev_names.push_back("c1 HDMI Jack");
eviocbit_ret[LONG(SW_LINEOUT_INSERT)] |= 1 << OFF(SW_LINEOUT_INSERT);
gpio_switch_eviocgbit_fd = 3;
snd_hctl_first_elem_return_val = NULL;
@@ -555,11 +620,13 @@ TEST(AlsaJacks, GPIOHdmiWithEdid) {
0,
1,
fake_mixer,
- reinterpret_cast<snd_use_case_mgr_t*>(0x55),
+ reinterpret_cast<struct cras_use_case_mgr *>(0x55),
+ fake_hctl,
CRAS_STREAM_OUTPUT,
fake_jack_cb,
fake_jack_cb_arg);
ASSERT_NE(static_cast<cras_alsa_jack_list*>(NULL), jack_list);
+ EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
EXPECT_EQ(1, gpio_switch_eviocgsw_called);
// EDID shouldn't open, callback should be skipped until re-try.
@@ -568,34 +635,34 @@ TEST(AlsaJacks, GPIOHdmiWithEdid) {
EXPECT_EQ(0, fake_jack_cb_called);
cras_alsa_jack_list_destroy(jack_list);
- EXPECT_EQ(1, gpio_get_switch_names_called);
+ EXPECT_EQ(1, gpio_switch_list_for_each_called);
EXPECT_GT(gpio_switch_open_called, 1);
EXPECT_GT(gpio_switch_eviocgbit_called, 1);
- EXPECT_GT(sys_input_get_device_name_called, 1);
EXPECT_EQ(1, cras_system_add_select_fd_called);
- EXPECT_EQ(1, snd_hctl_close_called);
+ EXPECT_EQ(1, cras_system_rm_select_fd_called);
}
TEST(AlsaJacks, CreateGPIOHpNoNameMatch) {
struct cras_alsa_jack_list *jack_list;
ResetStubData();
- gpio_get_switch_names_count = ~0;
+ gpio_switch_list_for_each_dev_names.push_back("some-other-device one");
+ gpio_switch_list_for_each_dev_names.push_back("some-other-device two");
snd_hctl_first_elem_return_val = NULL;
jack_list = cras_alsa_jack_list_create(0, "c2", 0, 1,
fake_mixer,
- NULL,
+ NULL, fake_hctl,
CRAS_STREAM_OUTPUT,
fake_jack_cb,
fake_jack_cb_arg);
ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), jack_list);
+ EXPECT_EQ(0, cras_alsa_jack_list_find_jacks_by_name_matching(jack_list));
cras_alsa_jack_list_destroy(jack_list);
- EXPECT_EQ(1, gpio_get_switch_names_called);
- EXPECT_GT(gpio_switch_open_called, 1);
- EXPECT_GT(sys_input_get_device_name_called, 1);
+ EXPECT_EQ(1, gpio_switch_list_for_each_called);
+ EXPECT_EQ(0, gpio_switch_open_called);
EXPECT_EQ(0, cras_system_add_select_fd_called);
- EXPECT_EQ(1, snd_hctl_close_called);
+ EXPECT_EQ(0, cras_system_rm_select_fd_called);
}
TEST(AlsaJacks, CreateOneHpJack) {
@@ -604,24 +671,20 @@ TEST(AlsaJacks, CreateOneHpJack) {
"Headphone Jack, klasdjf",
"Mic Jack",
};
- struct pollfd poll_fds[] = {
- {3, 0, 0},
- };
struct cras_alsa_jack_list *jack_list;
ResetStubData();
- snd_hctl_poll_descriptors_fds = poll_fds;
- snd_hctl_poll_descriptors_num_fds = ARRAY_SIZE(poll_fds);
jack_list = run_test_with_elem_list(CRAS_STREAM_OUTPUT,
elem_names,
0,
- NULL,
+ NULL,
ARRAY_SIZE(elem_names),
0,
1);
ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), jack_list);
- EXPECT_EQ(ARRAY_SIZE(poll_fds), cras_system_add_select_fd_called);
- EXPECT_EQ(3, cras_system_add_select_fd_values[0]);
+ ASSERT_NE(reinterpret_cast<snd_hctl_elem_callback_t>(NULL),
+ snd_hctl_elem_set_callback_value);
+ EXPECT_EQ(1, snd_hctl_elem_set_callback_called);
snd_hctl_elem_get_hctl_return_value = reinterpret_cast<snd_hctl_t *>(0x33);
snd_hctl_elem_get_name_called = 0;
@@ -641,9 +704,9 @@ TEST(AlsaJacks, CreateOneHpJack) {
EXPECT_EQ(1, fake_jack_cb_called);
cras_alsa_jack_list_destroy(jack_list);
- EXPECT_EQ(ARRAY_SIZE(poll_fds), cras_system_rm_select_fd_called);
- EXPECT_EQ(3, cras_system_rm_select_fd_values[0]);
- EXPECT_EQ(1, snd_hctl_close_called);
+ EXPECT_EQ(2, snd_hctl_elem_set_callback_called);
+ EXPECT_EQ(reinterpret_cast<snd_hctl_elem_callback_t>(NULL),
+ snd_hctl_elem_set_callback_value);
}
TEST(AlsaJacks, CreateOneMicJack) {
@@ -660,14 +723,20 @@ TEST(AlsaJacks, CreateOneMicJack) {
jack_list = run_test_with_elem_list(CRAS_STREAM_INPUT,
elem_names,
0,
- NULL,
+ NULL,
ARRAY_SIZE(elem_names),
0,
1);
ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), jack_list);
+ ASSERT_NE(reinterpret_cast<snd_hctl_elem_callback_t>(NULL),
+ snd_hctl_elem_set_callback_value);
+ EXPECT_EQ(1, snd_hctl_elem_set_callback_called);
cras_alsa_jack_list_destroy(jack_list);
- EXPECT_EQ(1, snd_hctl_close_called);
+ EXPECT_EQ(0, cras_system_rm_select_fd_called);
+ EXPECT_EQ(2, snd_hctl_elem_set_callback_called);
+ EXPECT_EQ(reinterpret_cast<snd_hctl_elem_callback_t>(NULL),
+ snd_hctl_elem_set_callback_value);
}
TEST(AlsaJacks, CreateHDMIJacksWithELD) {
@@ -677,14 +746,9 @@ TEST(AlsaJacks, CreateHDMIJacksWithELD) {
"ELD",
"HDMI/DP,pcm=4 Jack"
};
- struct pollfd poll_fds[] = {
- {0},
- };
struct cras_alsa_jack_list *jack_list;
ResetStubData();
- snd_hctl_poll_descriptors_fds = poll_fds;
- snd_hctl_poll_descriptors_num_fds = ARRAY_SIZE(poll_fds);
snd_hctl_elem_get_device_return_val = 3;
jack_list = run_test_with_elem_list(
@@ -700,7 +764,6 @@ TEST(AlsaJacks, CreateHDMIJacksWithELD) {
/* Assert get device is called for the ELD control */
EXPECT_EQ(1, snd_hctl_elem_get_device_called);
cras_alsa_jack_list_destroy(jack_list);
- EXPECT_EQ(1, snd_hctl_close_called);
}
TEST(AlsaJacks, CreateOneHpTwoHDMIJacks) {
@@ -711,26 +774,19 @@ TEST(AlsaJacks, CreateOneHpTwoHDMIJacks) {
"HDMI/DP,pcm=6 Jack",
"Mic Jack",
};
- struct pollfd poll_fds[] = {
- {5, 0, 0},
- };
struct cras_alsa_jack_list *jack_list;
ResetStubData();
- snd_hctl_poll_descriptors_fds = poll_fds;
- snd_hctl_poll_descriptors_num_fds = ARRAY_SIZE(poll_fds);
ucm_get_dev_for_jack_return = true;
jack_list = run_test_with_elem_list(
CRAS_STREAM_OUTPUT,
elem_names,
5,
- reinterpret_cast<snd_use_case_mgr_t*>(0x55),
+ reinterpret_cast<struct cras_use_case_mgr *>(0x55),
ARRAY_SIZE(elem_names),
1,
1);
ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), jack_list);
- EXPECT_EQ(ARRAY_SIZE(poll_fds), cras_system_add_select_fd_called);
- EXPECT_EQ(5, cras_system_add_select_fd_values[0]);
snd_hctl_elem_get_hctl_return_value = reinterpret_cast<snd_hctl_t *>(0x33);
snd_hctl_elem_get_name_called = 0;
@@ -750,9 +806,149 @@ TEST(AlsaJacks, CreateOneHpTwoHDMIJacks) {
EXPECT_EQ(1, fake_jack_cb_called);
cras_alsa_jack_list_destroy(jack_list);
- EXPECT_EQ(ARRAY_SIZE(poll_fds), cras_system_rm_select_fd_called);
- EXPECT_EQ(5, cras_system_rm_select_fd_values[0]);
- EXPECT_EQ(1, snd_hctl_close_called);
+}
+
+TEST(AlsaJacks, CreateHCTLHeadphoneJackFromUCM) {
+ std::string elem_names[] = {
+ "HP/DP,pcm=5 Jack",
+ "Headphone Jack",
+ };
+ struct cras_alsa_jack_list *jack_list;
+ struct ucm_section *section;
+
+ section = ucm_section_create("Headphone", 0, CRAS_STREAM_OUTPUT,
+ "Headphone Jack", "hctl");
+
+ ResetStubData();
+ ucm_get_dev_for_jack_return = true;
+
+ jack_list = run_test_with_section(
+ CRAS_STREAM_OUTPUT,
+ elem_names,
+ ARRAY_SIZE(elem_names),
+ 5,
+ reinterpret_cast<struct cras_use_case_mgr *>(0x55),
+ section,
+ 0,
+ 1);
+ ASSERT_NE(reinterpret_cast<struct cras_alsa_jack_list *>(NULL), jack_list);
+
+ snd_hctl_elem_get_hctl_return_value = reinterpret_cast<snd_hctl_t *>(0x33);
+ snd_ctl_elem_value_get_boolean_return_value = 1;
+ snd_hctl_elem_set_callback_value(
+ reinterpret_cast<snd_hctl_elem_t *>(&elem_names[1]), 0);
+ EXPECT_EQ(1, snd_hctl_elem_get_name_called);
+ EXPECT_EQ(1, fake_jack_cb_plugged);
+ EXPECT_EQ(1, fake_jack_cb_called);
+ EXPECT_EQ(fake_jack_cb_arg, fake_jack_cb_data);
+ EXPECT_EQ(reinterpret_cast<snd_hctl_elem_t *>(&elem_names[1]),
+ snd_hctl_elem_set_callback_obj);
+
+ fake_jack_cb_called = 0;
+ cras_alsa_jack_list_report(jack_list);
+ EXPECT_EQ(1, fake_jack_cb_plugged);
+ EXPECT_EQ(1, fake_jack_cb_called);
+
+ ucm_section_free_list(section);
+ cras_alsa_jack_list_destroy(jack_list);
+}
+
+TEST(AlsaJacks, CreateGPIOHeadphoneJackFromUCM) {
+ struct cras_alsa_jack_list *jack_list;
+ struct cras_alsa_jack *jack;
+ struct ucm_section *section;
+
+ section = ucm_section_create("Headphone", 0, CRAS_STREAM_OUTPUT,
+ "c1 Headphone Jack", "gpio");
+
+ ResetStubData();
+ gpio_switch_list_for_each_dev_names.push_back("some-other-device");
+ gpio_switch_list_for_each_dev_names.push_back("c1 Headphone Jack");
+ eviocbit_ret[LONG(SW_HEADPHONE_INSERT)] |= 1 << OFF(SW_HEADPHONE_INSERT);
+ gpio_switch_eviocgbit_fd = 2;
+ snd_hctl_first_elem_return_val = NULL;
+ jack_list = cras_alsa_jack_list_create(0, "c1", 0, 1,
+ fake_mixer,
+ NULL, fake_hctl,
+ CRAS_STREAM_OUTPUT,
+ fake_jack_cb,
+ fake_jack_cb_arg);
+ ASSERT_NE(static_cast<struct cras_alsa_jack_list *>(NULL), jack_list);
+ EXPECT_EQ(0, cras_alsa_jack_list_add_jack_for_section(
+ jack_list, section, &jack));
+ EXPECT_EQ(1, gpio_switch_list_for_each_called);
+ EXPECT_GT(gpio_switch_open_called, 1);
+ EXPECT_EQ(1, gpio_switch_eviocgsw_called);
+ EXPECT_GT(gpio_switch_eviocgbit_called, 1);
+ EXPECT_EQ(1, cras_system_add_select_fd_called);
+ EXPECT_EQ(1, cras_alsa_mixer_get_control_for_section_called);
+
+ fake_jack_cb_called = 0;
+ ucm_get_dev_for_jack_return = true;
+ cras_alsa_jack_list_report(jack_list);
+ EXPECT_EQ(1, fake_jack_cb_plugged);
+ EXPECT_EQ(1, fake_jack_cb_called);
+ EXPECT_EQ(fake_jack_cb_arg, fake_jack_cb_data);
+
+ ucm_section_free_list(section);
+ cras_alsa_jack_list_destroy(jack_list);
+ EXPECT_EQ(1, cras_system_rm_select_fd_called);
+}
+
+TEST(AlsaJacks, BadJackTypeFromUCM) {
+ std::string elem_names[] = {
+ "HP/DP,pcm=5 Jack",
+ "Headphone Jack",
+ };
+ struct cras_alsa_jack_list *jack_list;
+ struct ucm_section *section;
+
+ section = ucm_section_create("Headphone", 0, CRAS_STREAM_OUTPUT,
+ "Headphone Jack", "badtype");
+
+ ResetStubData();
+ ucm_get_dev_for_jack_return = true;
+
+ jack_list = run_test_with_section(
+ CRAS_STREAM_OUTPUT,
+ elem_names,
+ ARRAY_SIZE(elem_names),
+ 5,
+ reinterpret_cast<struct cras_use_case_mgr *>(0x55),
+ section,
+ -22,
+ 1);
+ EXPECT_EQ(reinterpret_cast<struct cras_alsa_jack_list *>(NULL), jack_list);
+
+ ucm_section_free_list(section);
+}
+
+TEST(AlsaJacks, NoJackTypeFromUCM) {
+ std::string elem_names[] = {
+ "HP/DP,pcm=5 Jack",
+ "Headphone Jack",
+ };
+ struct cras_alsa_jack_list *jack_list;
+ struct ucm_section *section;
+
+ section = ucm_section_create("Headphone", 0, CRAS_STREAM_OUTPUT,
+ "Headphone Jack", NULL);
+
+ ResetStubData();
+ ucm_get_dev_for_jack_return = true;
+
+ jack_list = run_test_with_section(
+ CRAS_STREAM_OUTPUT,
+ elem_names,
+ ARRAY_SIZE(elem_names),
+ 5,
+ reinterpret_cast<struct cras_use_case_mgr *>(0x55),
+ section,
+ -22,
+ 1);
+ EXPECT_EQ(reinterpret_cast<struct cras_alsa_jack_list *>(NULL), jack_list);
+
+ ucm_section_free_list(section);
}
/* Stubs */
@@ -775,19 +971,6 @@ void cras_system_rm_select_fd(int fd)
}
// From alsa-lib hcontrol.c
-int snd_hctl_open(snd_hctl_t **hctlp, const char *name, int mode) {
- *hctlp = snd_hctl_open_pointer_val;
- snd_hctl_open_called++;
- return snd_hctl_open_return_value;
-}
-int snd_hctl_load(snd_hctl_t *hctl) {
- snd_hctl_load_called++;
- return snd_hctl_load_return_value;
-}
-int snd_hctl_close(snd_hctl_t *hctl) {
- snd_hctl_close_called++;
- return 0;
-}
unsigned int snd_hctl_elem_get_device(const snd_hctl_elem_t *obj) {
snd_hctl_elem_get_device_called = 1;
return snd_hctl_elem_get_device_return_val;
@@ -826,21 +1009,6 @@ void snd_hctl_elem_set_callback(snd_hctl_elem_t *obj,
snd_hctl_elem_set_callback_obj = obj;
snd_hctl_elem_set_callback_value = val;
}
-int snd_hctl_poll_descriptors_count(snd_hctl_t *hctl) {
- return snd_hctl_poll_descriptors_num_fds;
-}
-int snd_hctl_poll_descriptors(snd_hctl_t *hctl,
- struct pollfd *pfds,
- unsigned int space) {
- unsigned int num = MIN(space, snd_hctl_poll_descriptors_num_fds);
- memcpy(pfds, snd_hctl_poll_descriptors_fds, num * sizeof(*pfds));
- snd_hctl_poll_descriptors_called++;
- return num;
-}
-int snd_hctl_handle_events(snd_hctl_t *hctl) {
- snd_hctl_handle_events_called++;
- return 0;
-}
void snd_hctl_elem_set_callback_private(snd_hctl_elem_t *obj, void * val) {
snd_hctl_elem_set_callback_private_called++;
snd_hctl_elem_set_callback_private_value = val;
@@ -855,10 +1023,29 @@ snd_hctl_t *snd_hctl_elem_get_hctl(snd_hctl_elem_t *elem) {
int snd_hctl_elem_read(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value) {
return 0;
}
-int snd_hctl_nonblock(snd_hctl_t *hctl, int nonblock) {
- snd_hctl_nonblock_called++;
- return 0;
+snd_hctl_elem_t *snd_hctl_find_elem(snd_hctl_t *hctl,
+ const snd_ctl_elem_id_t *id) {
+ const size_t* index = reinterpret_cast<const size_t*>(id);
+ snd_hctl_find_elem_called++;
+ if (*index < snd_hctl_find_elem_return_vals.size())
+ return snd_hctl_find_elem_return_vals[*index];
+ return NULL;
+}
+void snd_ctl_elem_id_set_interface(snd_ctl_elem_id_t *obj,
+ snd_ctl_elem_iface_t val) {
}
+void snd_ctl_elem_id_set_device(snd_ctl_elem_id_t *obj, unsigned int val) {
+}
+void snd_ctl_elem_id_set_name(snd_ctl_elem_id_t *obj, const char *val) {
+ size_t *obj_id = reinterpret_cast<size_t*>(obj);
+ std::map<std::string, size_t>::iterator id_name_it =
+ snd_ctl_elem_id_set_name_map.find(val);
+ if (id_name_it != snd_ctl_elem_id_set_name_map.end())
+ *obj_id = id_name_it->second;
+ else
+ *obj_id = INT_MAX;
+}
+
// From alsa-lib control.c
int snd_ctl_elem_value_get_boolean(const snd_ctl_elem_value_t *obj,
unsigned int idx) {
@@ -867,7 +1054,7 @@ int snd_ctl_elem_value_get_boolean(const snd_ctl_elem_value_t *obj,
}
// From cras_alsa_mixer
-struct mixer_output_control *cras_alsa_mixer_get_output_matching_name(
+struct mixer_control *cras_alsa_mixer_get_output_matching_name(
const struct cras_alsa_mixer *cras_mixer,
size_t device_index,
const char * const name)
@@ -876,7 +1063,7 @@ struct mixer_output_control *cras_alsa_mixer_get_output_matching_name(
return cras_alsa_mixer_get_output_matching_name_return_value;
}
-struct mixer_volume_control *cras_alsa_mixer_get_input_matching_name(
+struct mixer_control *cras_alsa_mixer_get_input_matching_name(
struct cras_alsa_mixer *cras_mixer,
const char *control_name)
{
@@ -884,10 +1071,12 @@ struct mixer_volume_control *cras_alsa_mixer_get_input_matching_name(
return cras_alsa_mixer_get_input_matching_name_return_value;
}
-char *sys_input_get_device_name(const char *path)
+struct mixer_control *cras_alsa_mixer_get_control_for_section(
+ struct cras_alsa_mixer *cras_mixer,
+ struct ucm_section *section)
{
- sys_input_get_device_name_called++;
- return strdup("c1 Headphone Jack");
+ cras_alsa_mixer_get_control_for_section_called++;
+ return cras_alsa_mixer_get_control_for_section_return_value;
}
int gpio_switch_eviocgbit(int fd, void *buf, size_t n_bytes)
@@ -925,6 +1114,7 @@ int gpio_switch_read(int fd, void *buf, size_t n_bytes)
* unittest.
*/
assert(0);
+ return 0;
}
int gpio_switch_open(const char *pathname)
@@ -937,38 +1127,33 @@ int gpio_switch_open(const char *pathname)
return 0;
}
-unsigned gpio_get_switch_names(enum CRAS_STREAM_DIRECTION direction,
- char **names, size_t n_names)
+void gpio_switch_list_for_each(gpio_switch_list_callback callback, void *arg)
{
- unsigned i, ub;
- static const char *dummy[] = {
- "/dev/input/event3",
- "/dev/input/event2",
- };
-
- ++gpio_get_switch_names_called;
+ size_t i = 0;
- ub = gpio_get_switch_names_count;
- if (ub > ARRAY_SIZE(dummy))
- ub = ARRAY_SIZE(dummy);
+ ++gpio_switch_list_for_each_called;
- for (i = 0; i < ub; ++i) {
- names[i] = strdup(dummy[i]);
+ while (i < gpio_switch_list_for_each_dev_names.size() &&
+ i < gpio_switch_list_for_each_dev_paths.size()) {
+ callback(gpio_switch_list_for_each_dev_paths[i].c_str(),
+ gpio_switch_list_for_each_dev_names[i].c_str(),
+ arg);
+ i++;
}
- return ub;
}
-int ucm_set_enabled(snd_use_case_mgr_t *mgr, const char *dev, int enable) {
+int ucm_set_enabled(
+ struct cras_use_case_mgr *mgr, const char *dev, int enable) {
ucm_set_enabled_value = enable;
return 0;
}
-char *ucm_get_cap_control(snd_use_case_mgr_t *mgr, const char *ucm_dev) {
+char *ucm_get_cap_control(struct cras_use_case_mgr *mgr, const char *ucm_dev) {
++ucm_get_cap_control_called;
return ucm_get_cap_control_value;
}
-char *ucm_get_dev_for_jack(snd_use_case_mgr_t *mgr, const char *jack,
+char *ucm_get_dev_for_jack(struct cras_use_case_mgr *mgr, const char *jack,
CRAS_STREAM_DIRECTION direction) {
++ucm_get_dev_for_jack_called;
if (ucm_get_dev_for_jack_return)
@@ -976,25 +1161,25 @@ char *ucm_get_dev_for_jack(snd_use_case_mgr_t *mgr, const char *jack,
return NULL;
}
-const char *ucm_get_dsp_name(snd_use_case_mgr_t *mgr, const char *ucm_dev,
+const char *ucm_get_dsp_name(struct cras_use_case_mgr *mgr, const char *ucm_dev,
int direction) {
++ucm_get_dsp_name_called;
return NULL;
}
-const char *ucm_get_edid_file_for_dev(snd_use_case_mgr_t *mgr,
+const char *ucm_get_edid_file_for_dev(struct cras_use_case_mgr *mgr,
const char *dev) {
return edid_file_ret;
}
-const char *ucm_get_override_type_name(snd_use_case_mgr_t *mgr,
+const char *ucm_get_override_type_name(struct cras_use_case_mgr *mgr,
const char *ucm_dev)
{
++ucm_get_override_type_name_called;
return NULL;
}
-const char *ucm_get_device_name_for_dev(snd_use_case_mgr_t *mgr,
+const char *ucm_get_device_name_for_dev(struct cras_use_case_mgr *mgr,
const char *dev,
enum CRAS_STREAM_DIRECTION direction)
{
@@ -1030,11 +1215,18 @@ int edid_get_monitor_name(const unsigned char *edid_data,
return 0;
}
+// Overwrite this function so unittest can run without 2 seconds of wait
+// in find_gpio_jacks.
+int wait_for_dev_input_access() {
+ return 0;
+}
+
} /* extern "C" */
} // namespace
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
+ openlog(NULL, LOG_PERROR, LOG_USER);
return RUN_ALL_TESTS();
}
diff --git a/cras/src/tests/alsa_mixer_unittest.cc b/cras/src/tests/alsa_mixer_unittest.cc
index 6820e265..c689835a 100644
--- a/cras/src/tests/alsa_mixer_unittest.cc
+++ b/cras/src/tests/alsa_mixer_unittest.cc
@@ -4,13 +4,18 @@
#include <stdio.h>
#include <gtest/gtest.h>
+#include <map>
+#include <string>
+#include <syslog.h>
#include <vector>
extern "C" {
#include "cras_alsa_mixer.h"
+#include "cras_alsa_mixer_name.h"
#include "cras_types.h"
#include "cras_util.h"
#include "cras_volume_curve.h"
+#include "utlist.h"
// Include C file to test static functions and use the definition of some
// structure.
@@ -66,7 +71,6 @@ static int snd_mixer_selem_get_playback_dB_return_values_length;
static int snd_mixer_selem_get_capture_dB_called;
static long *snd_mixer_selem_get_capture_dB_return_values;
static int snd_mixer_selem_get_capture_dB_return_values_length;
-static size_t cras_card_config_get_volume_curve_for_control_called;
static size_t cras_volume_curve_destroy_called;
static size_t snd_mixer_selem_get_playback_dB_range_called;
static size_t snd_mixer_selem_get_playback_dB_range_values_length;
@@ -79,6 +83,9 @@ static const long *snd_mixer_selem_get_capture_dB_range_max_values;
static size_t iniparser_getstring_return_index;
static size_t iniparser_getstring_return_length;
static char **iniparser_getstring_returns;
+static size_t snd_mixer_find_selem_called;
+static std::map<std::string, snd_mixer_elem_t*> snd_mixer_find_elem_map;
+static std::string snd_mixer_find_elem_id_name;
static void ResetStubData() {
iniparser_getstring_return_index = 0;
@@ -128,7 +135,6 @@ static void ResetStubData() {
snd_mixer_selem_get_capture_dB_called = 0;
snd_mixer_selem_get_capture_dB_return_values = static_cast<long *>(NULL);
snd_mixer_selem_get_capture_dB_return_values_length = 0;
- cras_card_config_get_volume_curve_for_control_called = 0;
cras_volume_curve_destroy_called = 0;
snd_mixer_selem_get_playback_dB_range_called = 0;
snd_mixer_selem_get_playback_dB_range_values_length = 0;
@@ -138,6 +144,19 @@ static void ResetStubData() {
snd_mixer_selem_get_capture_dB_range_values_length = 0;
snd_mixer_selem_get_capture_dB_range_min_values = static_cast<long *>(NULL);
snd_mixer_selem_get_capture_dB_range_max_values = static_cast<long *>(NULL);
+ snd_mixer_find_selem_called = 0;
+ snd_mixer_find_elem_map.clear();
+ snd_mixer_find_elem_id_name.clear();
+}
+
+struct cras_alsa_mixer *create_mixer_and_add_controls_by_name_matching(
+ const char *card_name,
+ struct mixer_name *extra_controls,
+ struct mixer_name *coupled_controls) {
+ struct cras_alsa_mixer *cmix = cras_alsa_mixer_create(card_name);
+ cras_alsa_mixer_add_controls_by_name_matching(
+ cmix, extra_controls, coupled_controls);
+ return cmix;
}
TEST(AlsaMixer, CreateFailOpen) {
@@ -145,10 +164,9 @@ TEST(AlsaMixer, CreateFailOpen) {
ResetStubData();
snd_mixer_open_return_value = -1;
- c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
- EXPECT_EQ(static_cast<struct cras_alsa_mixer *>(NULL), c);
+ c = cras_alsa_mixer_create("hw:0");
+ EXPECT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
- EXPECT_EQ(0, snd_mixer_close_called);
}
TEST(AlsaMixer, CreateFailAttach) {
@@ -156,8 +174,8 @@ TEST(AlsaMixer, CreateFailAttach) {
ResetStubData();
snd_mixer_attach_return_value = -1;
- c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
- EXPECT_EQ(static_cast<struct cras_alsa_mixer *>(NULL), c);
+ c = cras_alsa_mixer_create("hw:0");
+ EXPECT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
@@ -169,8 +187,8 @@ TEST(AlsaMixer, CreateFailSelemRegister) {
ResetStubData();
snd_mixer_selem_register_return_value = -1;
- c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
- EXPECT_EQ(static_cast<struct cras_alsa_mixer *>(NULL), c);
+ c = cras_alsa_mixer_create("hw:0");
+ EXPECT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
@@ -183,8 +201,8 @@ TEST(AlsaMixer, CreateFailLoad) {
ResetStubData();
snd_mixer_load_return_value = -1;
- c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
- EXPECT_EQ(static_cast<struct cras_alsa_mixer *>(NULL), c);
+ c = cras_alsa_mixer_create("hw:0");
+ EXPECT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
@@ -197,7 +215,8 @@ TEST(AlsaMixer, CreateNoElements) {
struct cras_alsa_mixer *c;
ResetStubData();
- c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
+ c = create_mixer_and_add_controls_by_name_matching(
+ "hw:0", NULL, NULL);
ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
@@ -222,12 +241,14 @@ TEST(AlsaMixer, CreateOneUnknownElementWithoutVolume) {
int element_playback_volume[] = {
0,
};
+ int element_playback_switches[] = {
+ 1,
+ };
const char *element_names[] = {
"Unknown",
};
- struct mixer_output_control output;
struct mixer_control *mixer_output;
- mixer_output = reinterpret_cast<mixer_control *>(&output);
+ int rc;
ResetStubData();
snd_mixer_first_elem_return_value = reinterpret_cast<snd_mixer_elem_t *>(1);
@@ -236,7 +257,8 @@ TEST(AlsaMixer, CreateOneUnknownElementWithoutVolume) {
ARRAY_SIZE(element_playback_volume);
snd_mixer_selem_get_name_return_values = element_names;
snd_mixer_selem_get_name_return_values_length = ARRAY_SIZE(element_names);
- c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
+ c = create_mixer_and_add_controls_by_name_matching(
+ "hw:0", NULL, NULL);
ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
@@ -251,9 +273,23 @@ TEST(AlsaMixer, CreateOneUnknownElementWithoutVolume) {
/* set mute shouldn't call anything. */
cras_alsa_mixer_set_mute(c, 0, NULL);
EXPECT_EQ(0, snd_mixer_selem_set_playback_switch_all_called);
+
+ ResetStubData();
+ snd_mixer_selem_has_playback_switch_return_values = element_playback_switches;
+ snd_mixer_selem_has_playback_switch_return_values_length =
+ ARRAY_SIZE(element_playback_switches);
+ snd_mixer_selem_get_name_return_values = element_names;
+ snd_mixer_selem_get_name_return_values_length = ARRAY_SIZE(element_names);
+ rc = mixer_control_create(&mixer_output, NULL,
+ reinterpret_cast<snd_mixer_elem_t *>(1),
+ CRAS_STREAM_OUTPUT);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, snd_mixer_selem_get_name_called);
+ EXPECT_EQ(1, snd_mixer_selem_has_playback_volume_called);
+ EXPECT_EQ(1, snd_mixer_selem_has_playback_switch_called);
+ EXPECT_EQ(1, snd_mixer_selem_get_playback_dB_range_called);
+
/* if passed a mixer output then it should mute that. */
- mixer_output->elem = reinterpret_cast<snd_mixer_elem_t *>(0x454);
- mixer_output->has_mute = 1;
cras_alsa_mixer_set_mute(c, 0, mixer_output);
EXPECT_EQ(1, snd_mixer_selem_set_playback_switch_all_called);
/* set volume shouldn't call anything. */
@@ -262,6 +298,7 @@ TEST(AlsaMixer, CreateOneUnknownElementWithoutVolume) {
cras_alsa_mixer_destroy(c);
EXPECT_EQ(1, snd_mixer_close_called);
+ mixer_control_destroy(mixer_output);
}
TEST(AlsaMixer, CreateOneUnknownElementWithVolume) {
@@ -270,13 +307,18 @@ TEST(AlsaMixer, CreateOneUnknownElementWithVolume) {
static const long max_volumes[] = {40};
int element_playback_volume[] = {
1,
+ 0,
+ };
+ int element_playback_switches[] = {
+ 0,
+ 1,
};
const char *element_names[] = {
"Unknown",
+ "Playback",
};
- struct mixer_output_control output;
struct mixer_control *mixer_output;
- mixer_output = reinterpret_cast<mixer_control *>(&output);
+ int rc;
ResetStubData();
snd_mixer_first_elem_return_value = reinterpret_cast<snd_mixer_elem_t *>(1);
@@ -288,7 +330,8 @@ TEST(AlsaMixer, CreateOneUnknownElementWithVolume) {
snd_mixer_selem_get_playback_dB_range_min_values = min_volumes;
snd_mixer_selem_get_playback_dB_range_max_values = max_volumes;
snd_mixer_selem_get_playback_dB_range_values_length = ARRAY_SIZE(min_volumes);
- c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
+ c = create_mixer_and_add_controls_by_name_matching(
+ "hw:0", NULL, NULL);
ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
@@ -296,16 +339,36 @@ TEST(AlsaMixer, CreateOneUnknownElementWithVolume) {
EXPECT_EQ(1, snd_mixer_selem_register_called);
EXPECT_EQ(1, snd_mixer_load_called);
EXPECT_EQ(0, snd_mixer_close_called);
- EXPECT_EQ(2, snd_mixer_selem_has_playback_volume_called);
+ EXPECT_EQ(3, snd_mixer_selem_has_playback_volume_called);
EXPECT_EQ(2, snd_mixer_selem_get_playback_dB_range_called);
- EXPECT_EQ(1, snd_mixer_selem_get_name_called);
+ EXPECT_EQ(3, snd_mixer_selem_get_name_called);
- /* should use the unknown element as a fallback */
+ /* Should use "Playback" since it has playback switch. */
cras_alsa_mixer_set_mute(c, 0, NULL);
EXPECT_EQ(1, snd_mixer_selem_set_playback_switch_all_called);
- /* if passed a mixer output then it should mute that. */
- mixer_output->elem = reinterpret_cast<snd_mixer_elem_t *>(0x454);
- mixer_output->has_mute = 1;
+
+ ResetStubData();
+ snd_mixer_selem_has_playback_volume_return_values = element_playback_volume;
+ snd_mixer_selem_has_playback_volume_return_values_length =
+ ARRAY_SIZE(element_playback_volume);
+ snd_mixer_selem_has_playback_switch_return_values = element_playback_switches;
+ snd_mixer_selem_has_playback_switch_return_values_length =
+ ARRAY_SIZE(element_playback_switches);
+ snd_mixer_selem_get_name_return_values = element_names;
+ snd_mixer_selem_get_name_return_values_length = ARRAY_SIZE(element_names);
+ rc = mixer_control_create(&mixer_output, NULL,
+ reinterpret_cast<snd_mixer_elem_t *>(2),
+ CRAS_STREAM_OUTPUT);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, snd_mixer_selem_get_name_called);
+ EXPECT_EQ(1, snd_mixer_selem_has_playback_volume_called);
+ EXPECT_EQ(1, snd_mixer_selem_has_playback_switch_called);
+ EXPECT_EQ(0, snd_mixer_selem_get_playback_dB_range_called);
+
+ /*
+ * If passed a mixer output then it should mute both "Playback" and that
+ * mixer_output.
+ */
cras_alsa_mixer_set_mute(c, 0, mixer_output);
EXPECT_EQ(2, snd_mixer_selem_set_playback_switch_all_called);
cras_alsa_mixer_set_dBFS(c, 0, NULL);
@@ -313,27 +376,28 @@ TEST(AlsaMixer, CreateOneUnknownElementWithVolume) {
cras_alsa_mixer_destroy(c);
EXPECT_EQ(1, snd_mixer_close_called);
+ mixer_control_destroy(mixer_output);
}
TEST(AlsaMixer, CreateOneMasterElement) {
struct cras_alsa_mixer *c;
int element_playback_volume[] = {
1,
+ 1,
};
int element_playback_switches[] = {
1,
+ 1,
};
const char *element_names[] = {
"Master",
+ "Playback"
};
- struct mixer_output_control output;
struct mixer_control *mixer_output;
- mixer_output = reinterpret_cast<mixer_control *>(&output);
- mixer_output->elem = reinterpret_cast<snd_mixer_elem_t *>(2);
- mixer_output->has_mute = 1;
- mixer_output->has_volume = 1;
- output.max_volume_dB = 950;
+ int rc;
long set_dB_values[3];
+ static const long min_volumes[] = {0, 0};
+ static const long max_volumes[] = {950, 950};
ResetStubData();
snd_mixer_first_elem_return_value = reinterpret_cast<snd_mixer_elem_t *>(1);
@@ -345,7 +409,8 @@ TEST(AlsaMixer, CreateOneMasterElement) {
ARRAY_SIZE(element_playback_switches);
snd_mixer_selem_get_name_return_values = element_names;
snd_mixer_selem_get_name_return_values_length = ARRAY_SIZE(element_names);
- c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
+ c = create_mixer_and_add_controls_by_name_matching(
+ "hw:0", NULL, NULL);
ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
@@ -353,7 +418,7 @@ TEST(AlsaMixer, CreateOneMasterElement) {
EXPECT_EQ(1, snd_mixer_selem_register_called);
EXPECT_EQ(1, snd_mixer_load_called);
EXPECT_EQ(0, snd_mixer_close_called);
- EXPECT_EQ(1, snd_mixer_selem_get_name_called);
+ EXPECT_EQ(3, snd_mixer_selem_get_name_called);
EXPECT_EQ(1, snd_mixer_elem_next_called);
/* set mute should be called for Master. */
@@ -363,12 +428,28 @@ TEST(AlsaMixer, CreateOneMasterElement) {
cras_alsa_mixer_set_dBFS(c, 0, NULL);
EXPECT_EQ(1, snd_mixer_selem_set_playback_dB_all_called);
- /* if passed a mixer output then it should set the volume for that too. */
+ ResetStubData();
snd_mixer_selem_set_playback_dB_all_values = set_dB_values;
snd_mixer_selem_set_playback_dB_all_values_length =
ARRAY_SIZE(set_dB_values);
- snd_mixer_selem_set_playback_dB_all_called = 0;
- snd_mixer_selem_get_playback_dB_called = 0;
+ snd_mixer_selem_get_playback_dB_range_min_values = min_volumes;
+ snd_mixer_selem_get_playback_dB_range_max_values = max_volumes;
+ snd_mixer_selem_get_playback_dB_range_values_length = ARRAY_SIZE(min_volumes);
+ snd_mixer_selem_has_playback_volume_return_values = element_playback_volume;
+ snd_mixer_selem_has_playback_volume_return_values_length =
+ ARRAY_SIZE(element_playback_volume);
+ snd_mixer_selem_get_name_return_values = element_names;
+ snd_mixer_selem_get_name_return_values_length = ARRAY_SIZE(element_names);
+ rc = mixer_control_create(&mixer_output, NULL,
+ reinterpret_cast<snd_mixer_elem_t *>(2),
+ CRAS_STREAM_OUTPUT);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, snd_mixer_selem_get_name_called);
+ EXPECT_EQ(1, snd_mixer_selem_has_playback_volume_called);
+ EXPECT_EQ(1, snd_mixer_selem_has_playback_switch_called);
+ EXPECT_EQ(1, snd_mixer_selem_get_playback_dB_range_called);
+
+ /* if passed a mixer output then it should set the volume for that too. */
cras_alsa_mixer_set_dBFS(c, 0, mixer_output);
EXPECT_EQ(2, snd_mixer_selem_set_playback_dB_all_called);
EXPECT_EQ(950, set_dB_values[0]);
@@ -376,6 +457,7 @@ TEST(AlsaMixer, CreateOneMasterElement) {
cras_alsa_mixer_destroy(c);
EXPECT_EQ(1, snd_mixer_close_called);
+ mixer_control_destroy(mixer_output);
}
TEST(AlsaMixer, CreateTwoMainVolumeElements) {
@@ -386,21 +468,20 @@ TEST(AlsaMixer, CreateTwoMainVolumeElements) {
int element_playback_volume[] = {
1,
1,
+ 1,
};
int element_playback_switches[] = {
1,
1,
+ 1,
};
const char *element_names[] = {
"Master",
"PCM",
+ "Other",
};
- struct mixer_output_control output;
struct mixer_control *mixer_output;
- mixer_output = reinterpret_cast<mixer_control *>(&output);
- mixer_output->elem = reinterpret_cast<snd_mixer_elem_t *>(3);
- mixer_output->has_volume = 1;
- output.max_volume_dB = 0;
+ int rc;
static const long min_volumes[] = {-500, -1250, -500};
static const long max_volumes[] = {40, 40, 0};
long get_dB_returns[] = {0, 0, 0};
@@ -425,9 +506,10 @@ TEST(AlsaMixer, CreateTwoMainVolumeElements) {
snd_mixer_selem_set_playback_dB_all_values = set_dB_values;
snd_mixer_selem_set_playback_dB_all_values_length =
ARRAY_SIZE(set_dB_values);
- c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
+ c = create_mixer_and_add_controls_by_name_matching(
+ "hw:0", NULL, NULL);
ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
- EXPECT_EQ(3, snd_mixer_selem_get_playback_dB_range_called);
+ EXPECT_EQ(2, snd_mixer_selem_get_playback_dB_range_called);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
@@ -435,8 +517,8 @@ TEST(AlsaMixer, CreateTwoMainVolumeElements) {
EXPECT_EQ(1, snd_mixer_load_called);
EXPECT_EQ(0, snd_mixer_close_called);
EXPECT_EQ(2, snd_mixer_elem_next_called);
- EXPECT_EQ(2, snd_mixer_selem_get_name_called);
- EXPECT_EQ(1, snd_mixer_selem_has_playback_switch_called);
+ EXPECT_EQ(5, snd_mixer_selem_get_name_called);
+ EXPECT_EQ(3, snd_mixer_selem_has_playback_switch_called);
/* Set mute should be called for Master only. */
cras_alsa_mixer_set_mute(c, 0, NULL);
@@ -456,11 +538,33 @@ TEST(AlsaMixer, CreateTwoMainVolumeElements) {
/* volume should be set relative to max volume (40 + 40). */
EXPECT_EQ(30, set_dB_values[0]);
EXPECT_EQ(30, set_dB_values[1]);
+
+ ResetStubData();
+ snd_mixer_selem_set_playback_dB_all_values = set_dB_values;
+ snd_mixer_selem_set_playback_dB_all_values_length = ARRAY_SIZE(set_dB_values);
+ snd_mixer_selem_get_playback_dB_range_min_values = min_volumes;
+ snd_mixer_selem_get_playback_dB_range_max_values = max_volumes;
+ snd_mixer_selem_get_playback_dB_range_values_length = ARRAY_SIZE(min_volumes);
+ snd_mixer_selem_has_playback_volume_return_values = element_playback_volume;
+ snd_mixer_selem_has_playback_volume_return_values_length =
+ ARRAY_SIZE(element_playback_volume);
+ snd_mixer_selem_has_playback_switch_return_values = element_playback_switches;
+ snd_mixer_selem_has_playback_switch_return_values_length =
+ ARRAY_SIZE(element_playback_switches);
+ snd_mixer_selem_get_name_return_values = element_names;
+ snd_mixer_selem_get_name_return_values_length = ARRAY_SIZE(element_names);
+ rc = mixer_control_create(&mixer_output, NULL,
+ reinterpret_cast<snd_mixer_elem_t *>(3),
+ CRAS_STREAM_OUTPUT);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, snd_mixer_selem_get_name_called);
+ EXPECT_EQ(1, snd_mixer_selem_has_playback_volume_called);
+ EXPECT_EQ(1, snd_mixer_selem_has_playback_switch_called);
+ EXPECT_EQ(1, snd_mixer_selem_get_playback_dB_range_called);
+
/* Set volume should be called for Master, PCM, and the mixer_output passed
* in. If Master doesn't set to anything but zero then the entire volume
* should be passed to the PCM control.*/
- snd_mixer_selem_set_playback_dB_all_called = 0;
- snd_mixer_selem_get_playback_dB_called = 0;
cras_alsa_mixer_set_dBFS(c, -50, mixer_output);
EXPECT_EQ(3, snd_mixer_selem_set_playback_dB_all_called);
EXPECT_EQ(2, snd_mixer_selem_get_playback_dB_called);
@@ -480,6 +584,8 @@ TEST(AlsaMixer, CreateTwoMainVolumeElements) {
snd_mixer_selem_set_playback_dB_all_called = 0;
snd_mixer_selem_get_playback_dB_called = 0;
mixer_output->has_volume = 0;
+ mixer_output->min_volume_dB = MIXER_CONTROL_VOLUME_DB_INVALID;
+ mixer_output->max_volume_dB = MIXER_CONTROL_VOLUME_DB_INVALID;
cras_alsa_mixer_set_dBFS(c, -50, mixer_output);
EXPECT_EQ(2, snd_mixer_selem_set_playback_dB_all_called);
EXPECT_EQ(2, snd_mixer_selem_get_playback_dB_called);
@@ -488,6 +594,7 @@ TEST(AlsaMixer, CreateTwoMainVolumeElements) {
cras_alsa_mixer_destroy(c);
EXPECT_EQ(1, snd_mixer_close_called);
+ mixer_control_destroy(mixer_output);
}
TEST(AlsaMixer, CreateTwoMainCaptureElements) {
@@ -511,8 +618,7 @@ TEST(AlsaMixer, CreateTwoMainCaptureElements) {
"Mic",
};
struct mixer_control *mixer_input;
- mixer_input = (struct mixer_control *)calloc(1, sizeof(*mixer_input));
- mixer_input->elem = reinterpret_cast<snd_mixer_elem_t *>(3);
+ int rc;
ResetStubData();
snd_mixer_first_elem_return_value = reinterpret_cast<snd_mixer_elem_t *>(1);
@@ -526,7 +632,8 @@ TEST(AlsaMixer, CreateTwoMainCaptureElements) {
ARRAY_SIZE(element_capture_switches);
snd_mixer_selem_get_name_return_values = element_names;
snd_mixer_selem_get_name_return_values_length = ARRAY_SIZE(element_names);
- c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
+ c = create_mixer_and_add_controls_by_name_matching(
+ "hw:0", NULL, NULL);
ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
@@ -535,8 +642,8 @@ TEST(AlsaMixer, CreateTwoMainCaptureElements) {
EXPECT_EQ(1, snd_mixer_load_called);
EXPECT_EQ(0, snd_mixer_close_called);
EXPECT_EQ(2, snd_mixer_elem_next_called);
- EXPECT_EQ(4, snd_mixer_selem_get_name_called);
- EXPECT_EQ(1, snd_mixer_selem_has_capture_switch_called);
+ EXPECT_EQ(5, snd_mixer_selem_get_name_called);
+ EXPECT_EQ(3, snd_mixer_selem_has_capture_switch_called);
/* Set mute should be called for Master only. */
cras_alsa_mixer_set_capture_mute(c, 0, NULL);
@@ -586,17 +693,39 @@ TEST(AlsaMixer, CreateTwoMainCaptureElements) {
long get_dB_returns3[] = {
0,
0,
+ 0,
};
long set_dB_values3[3];
- mixer_input->has_volume = 1;
+
snd_mixer_selem_get_capture_dB_return_values = get_dB_returns3;
snd_mixer_selem_get_capture_dB_return_values_length =
ARRAY_SIZE(get_dB_returns3);
+ snd_mixer_selem_get_capture_dB_called = 0;
snd_mixer_selem_set_capture_dB_all_values = set_dB_values3;
snd_mixer_selem_set_capture_dB_all_values_length =
ARRAY_SIZE(set_dB_values3);
snd_mixer_selem_set_capture_dB_all_called = 0;
- snd_mixer_selem_get_capture_dB_called = 0;
+ snd_mixer_selem_has_capture_volume_return_values = element_capture_volume;
+ snd_mixer_selem_has_capture_volume_return_values_length =
+ ARRAY_SIZE(element_capture_volume);
+ snd_mixer_selem_has_capture_switch_return_values = element_capture_switches;
+ snd_mixer_selem_has_capture_switch_return_values_length =
+ ARRAY_SIZE(element_capture_switches);
+ snd_mixer_selem_get_name_return_values = element_names;
+ snd_mixer_selem_get_name_return_values_length = ARRAY_SIZE(element_names);
+ snd_mixer_selem_get_name_called = 0;
+ snd_mixer_selem_has_capture_volume_called = 0;
+ snd_mixer_selem_has_capture_switch_called = 0;
+ snd_mixer_selem_get_capture_dB_range_called = 0;
+ rc = mixer_control_create(&mixer_input, NULL,
+ reinterpret_cast<snd_mixer_elem_t *>(3),
+ CRAS_STREAM_INPUT);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, snd_mixer_selem_get_name_called);
+ EXPECT_EQ(1, snd_mixer_selem_has_capture_volume_called);
+ EXPECT_EQ(1, snd_mixer_selem_has_capture_switch_called);
+ EXPECT_EQ(1, snd_mixer_selem_get_capture_dB_range_called);
+ EXPECT_EQ(1, mixer_input->has_volume);
cras_alsa_mixer_set_capture_dBFS(c, 20, mixer_input);
@@ -608,7 +737,7 @@ TEST(AlsaMixer, CreateTwoMainCaptureElements) {
cras_alsa_mixer_destroy(c);
EXPECT_EQ(1, snd_mixer_close_called);
- free(mixer_input);
+ mixer_control_destroy(mixer_input);
}
class AlsaMixerOutputs : public testing::Test {
@@ -651,6 +780,8 @@ class AlsaMixerOutputs : public testing::Test {
1,
1,
};
+ static const long min_volumes[] = {0, 0, 0, 0, 0, 0, 500, -1250};
+ static const long max_volumes[] = {0, 0, 0, 0, 0, 0, 3000, 400};
static const char *element_names[] = {
"Master",
"PCM",
@@ -667,6 +798,11 @@ class AlsaMixerOutputs : public testing::Test {
static char *iniparser_returns[] = {
NULL,
};
+ struct mixer_name *extra_controls =
+ mixer_name_add_array(NULL, output_names_extra,
+ ARRAY_SIZE(output_names_extra),
+ CRAS_STREAM_OUTPUT,
+ MIXER_NAME_VOLUME);
ResetStubData();
snd_mixer_first_elem_return_value =
@@ -691,11 +827,15 @@ class AlsaMixerOutputs : public testing::Test {
ARRAY_SIZE(element_capture_switches);
snd_mixer_selem_get_name_return_values = element_names;
snd_mixer_selem_get_name_return_values_length = ARRAY_SIZE(element_names);
+ snd_mixer_selem_get_capture_dB_range_called = 0;
+ snd_mixer_selem_get_capture_dB_range_min_values = min_volumes;
+ snd_mixer_selem_get_capture_dB_range_max_values = max_volumes;
+ snd_mixer_selem_get_capture_dB_range_values_length =
+ ARRAY_SIZE(min_volumes);
iniparser_getstring_returns = iniparser_returns;
iniparser_getstring_return_length = ARRAY_SIZE(iniparser_returns);
- cras_mixer_ = cras_alsa_mixer_create("hw:0",
- reinterpret_cast<struct cras_card_config*>(5),
- output_names_extra, ARRAY_SIZE(output_names_extra), NULL);
+ cras_mixer_ = create_mixer_and_add_controls_by_name_matching(
+ "hw:0", extra_controls, NULL);
ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), cras_mixer_);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
@@ -704,11 +844,11 @@ class AlsaMixerOutputs : public testing::Test {
EXPECT_EQ(1, snd_mixer_load_called);
EXPECT_EQ(0, snd_mixer_close_called);
EXPECT_EQ(ARRAY_SIZE(elements) + 1, snd_mixer_elem_next_called);
- EXPECT_EQ(6, snd_mixer_selem_has_playback_volume_called);
- EXPECT_EQ(5, snd_mixer_selem_has_playback_switch_called);
- EXPECT_EQ(2, snd_mixer_selem_has_capture_volume_called);
- EXPECT_EQ(1, snd_mixer_selem_has_capture_switch_called);
- EXPECT_EQ(5, cras_card_config_get_volume_curve_for_control_called);
+ EXPECT_EQ(8, snd_mixer_selem_has_playback_volume_called);
+ EXPECT_EQ(7, snd_mixer_selem_has_playback_switch_called);
+ EXPECT_EQ(4, snd_mixer_selem_has_capture_volume_called);
+ EXPECT_EQ(3, snd_mixer_selem_has_capture_switch_called);
+ mixer_name_free(extra_controls);
}
virtual void TearDown() {
@@ -740,36 +880,31 @@ TEST_F(AlsaMixerOutputs, CheckFourOutputs) {
TEST_F(AlsaMixerOutputs, CheckFindOutputByNameNoMatch) {
struct mixer_control *out;
- snd_mixer_selem_get_name_called = 0;
out = cras_alsa_mixer_get_output_matching_name(cras_mixer_,
"AAAAA Jack");
EXPECT_EQ(static_cast<struct mixer_control *>(NULL), out);
- EXPECT_EQ(4, snd_mixer_selem_get_name_called);
}
TEST_F(AlsaMixerOutputs, CheckFindOutputByName) {
struct mixer_control *out;
- snd_mixer_selem_get_name_called = 0;
out = cras_alsa_mixer_get_output_matching_name(cras_mixer_,
"Headphone Jack");
EXPECT_NE(static_cast<struct mixer_control *>(NULL), out);
- EXPECT_EQ(1, snd_mixer_selem_get_name_called);
}
TEST_F(AlsaMixerOutputs, CheckFindOutputHDMIByName) {
struct mixer_control *out;
- snd_mixer_selem_get_name_called = 0;
out = cras_alsa_mixer_get_output_matching_name(cras_mixer_,
"HDMI Jack");
EXPECT_NE(static_cast<struct mixer_control *>(NULL), out);
- EXPECT_EQ(3, snd_mixer_selem_get_name_called);
}
-TEST_F(AlsaMixerOutputs, CheckFindInputName) {
+TEST_F(AlsaMixerOutputs, CheckFindInputNameWorkaround) {
struct mixer_control *control;
snd_mixer_elem_t *elements[] = {
+ reinterpret_cast<snd_mixer_elem_t *>(1), // Speaker
reinterpret_cast<snd_mixer_elem_t *>(2), // Headphone
reinterpret_cast<snd_mixer_elem_t *>(3), // MIC
};
@@ -778,11 +913,11 @@ TEST_F(AlsaMixerOutputs, CheckFindInputName) {
"Headphone",
"MIC",
};
+ size_t i;
- snd_mixer_first_elem_return_value = reinterpret_cast<snd_mixer_elem_t *>(1);
- snd_mixer_elem_next_return_values = elements;
- snd_mixer_elem_next_return_values_index = 0;
- snd_mixer_elem_next_return_values_length = ARRAY_SIZE(elements);
+ ResetStubData();
+ for (i = 0; i < ARRAY_SIZE(elements); i++)
+ snd_mixer_find_elem_map[element_names[i]] = elements[i];
snd_mixer_selem_get_name_called = 0;
snd_mixer_selem_get_name_return_values = element_names;
@@ -790,8 +925,12 @@ TEST_F(AlsaMixerOutputs, CheckFindInputName) {
control = cras_alsa_mixer_get_input_matching_name(cras_mixer_,
"MIC");
EXPECT_NE(static_cast<struct mixer_control *>(NULL), control);
- /* 3 + 1, one more for log */
- EXPECT_EQ(4, snd_mixer_selem_get_name_called);
+ /* This exercises the 'workaround' where the control is added if it was
+ * previouly missing in cras_alsa_mixer_get_input_matching_name().
+ * snd_mixer_find_selem is called once for the missing control. */
+ EXPECT_EQ(1, snd_mixer_find_selem_called);
+ EXPECT_EQ(1, snd_mixer_selem_has_capture_volume_called);
+ EXPECT_EQ(1, snd_mixer_selem_has_capture_switch_called);
}
TEST_F(AlsaMixerOutputs, ActivateDeactivate) {
@@ -812,13 +951,6 @@ TEST_F(AlsaMixerOutputs, ActivateDeactivate) {
TEST_F(AlsaMixerOutputs, MinMaxCaptureGain) {
long min, max;
- static const long min_volumes[] = {0, 0, 0, 0, 0, 0, 500, -1250};
- static const long max_volumes[] = {0, 0, 0, 0, 0, 0, 3000, 400};
-
- snd_mixer_selem_get_capture_dB_range_called = 0;
- snd_mixer_selem_get_capture_dB_range_min_values = min_volumes;
- snd_mixer_selem_get_capture_dB_range_max_values = max_volumes;
- snd_mixer_selem_get_capture_dB_range_values_length = ARRAY_SIZE(min_volumes);
min = cras_alsa_mixer_get_minimum_capture_gain(cras_mixer_,
NULL);
EXPECT_EQ(-750, min);
@@ -831,16 +963,10 @@ TEST_F(AlsaMixerOutputs, MinMaxCaptureGainWithActiveInput) {
struct mixer_control *mixer_input;
long min, max;
- static const long min_volumes[] = {0, 0, 0, 0, 0, 0, 500, -1250, 50};
- static const long max_volumes[] = {0, 0, 0, 0, 0, 0, 3000, 400, 60};
-
- snd_mixer_selem_get_capture_dB_range_called = 0;
- snd_mixer_selem_get_capture_dB_range_min_values = min_volumes;
- snd_mixer_selem_get_capture_dB_range_max_values = max_volumes;
- snd_mixer_selem_get_capture_dB_range_values_length = ARRAY_SIZE(min_volumes);
-
mixer_input = (struct mixer_control *)calloc(1, sizeof(*mixer_input));
- mixer_input->elem = reinterpret_cast<snd_mixer_elem_t *>(9);
+ mixer_input->min_volume_dB = 50;
+ mixer_input->max_volume_dB = 60;
+ mixer_input->has_volume = 1;
min = cras_alsa_mixer_get_minimum_capture_gain(cras_mixer_, mixer_input);
max = cras_alsa_mixer_get_maximum_capture_gain(cras_mixer_, mixer_input);
EXPECT_EQ(-700, min);
@@ -849,6 +975,556 @@ TEST_F(AlsaMixerOutputs, MinMaxCaptureGainWithActiveInput) {
free((void *)mixer_input);
}
+TEST(AlsaMixer, CreateWithCoupledOutputControls) {
+ struct cras_alsa_mixer *c;
+ struct mixer_control *output_control;
+ struct mixer_control_element *c1, *c2, *c3, *c4;
+
+ static const long min_volumes[] = {-70, -70};
+ static const long max_volumes[] = {30, 30};
+
+ long set_dB_values[2];
+
+ const char *coupled_output_names[] = {"Left Master",
+ "Right Master",
+ "Left Speaker",
+ "Right Speaker"};
+ struct mixer_name *coupled_controls =
+ mixer_name_add_array(NULL, coupled_output_names,
+ ARRAY_SIZE(coupled_output_names),
+ CRAS_STREAM_OUTPUT,
+ MIXER_NAME_VOLUME);
+ int element_playback_volume[] = {1, 1, 0, 0};
+ int element_playback_switches[] = {0, 0, 1, 1};
+
+ long target_dBFS = -30;
+ long expected_dB_value = target_dBFS + max_volumes[0];
+
+ ResetStubData();
+
+ snd_mixer_find_elem_map[std::string("Left Master")] =
+ reinterpret_cast<snd_mixer_elem_t *>(1);
+ snd_mixer_find_elem_map[std::string("Right Master")] =
+ reinterpret_cast<snd_mixer_elem_t *>(2);
+ snd_mixer_find_elem_map[std::string("Left Speaker")] =
+ reinterpret_cast<snd_mixer_elem_t *>(3);
+ snd_mixer_find_elem_map[std::string("Right Speaker")] =
+ reinterpret_cast<snd_mixer_elem_t *>(4);
+
+ snd_mixer_selem_has_playback_volume_return_values = element_playback_volume;
+ snd_mixer_selem_has_playback_volume_return_values_length =
+ ARRAY_SIZE(element_playback_volume);
+ snd_mixer_selem_has_playback_switch_return_values = element_playback_switches;
+ snd_mixer_selem_has_playback_switch_return_values_length =
+ ARRAY_SIZE(element_playback_switches);
+
+ snd_mixer_selem_get_playback_dB_range_min_values = min_volumes;
+ snd_mixer_selem_get_playback_dB_range_max_values = max_volumes;
+ snd_mixer_selem_get_playback_dB_range_values_length = ARRAY_SIZE(min_volumes);
+
+ c = create_mixer_and_add_controls_by_name_matching(
+ "hw:0", NULL, coupled_controls);
+
+ ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
+ EXPECT_EQ(1, snd_mixer_open_called);
+ EXPECT_EQ(1, snd_mixer_attach_called);
+ EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
+ EXPECT_EQ(1, snd_mixer_selem_register_called);
+ EXPECT_EQ(1, snd_mixer_load_called);
+ EXPECT_EQ(0, snd_mixer_close_called);
+
+ output_control = c->output_controls;
+ EXPECT_EQ(NULL, output_control->next);
+ c1 = output_control->elements;
+ c2 = c1->next;
+ c3 = c2->next;
+ c4 = c3->next;
+ EXPECT_EQ(c1->elem, reinterpret_cast<snd_mixer_elem_t *>(1));
+ EXPECT_EQ(c2->elem, reinterpret_cast<snd_mixer_elem_t *>(2));
+ EXPECT_EQ(c3->elem, reinterpret_cast<snd_mixer_elem_t *>(3));
+ EXPECT_EQ(c4->elem, reinterpret_cast<snd_mixer_elem_t *>(4));
+ EXPECT_EQ(c4->next, reinterpret_cast<mixer_control_element *>(NULL));
+ EXPECT_EQ(c1->has_volume, 1);
+ EXPECT_EQ(c1->has_mute, 0);
+ EXPECT_EQ(c2->has_volume, 1);
+ EXPECT_EQ(c2->has_mute, 0);
+ EXPECT_EQ(c3->has_volume, 0);
+ EXPECT_EQ(c3->has_mute, 1);
+ EXPECT_EQ(c4->has_volume, 0);
+ EXPECT_EQ(c4->has_mute, 1);
+ EXPECT_EQ(output_control->max_volume_dB, max_volumes[0]);
+ EXPECT_EQ(output_control->min_volume_dB, min_volumes[0]);
+
+ snd_mixer_selem_set_playback_dB_all_values = set_dB_values;
+ snd_mixer_selem_set_playback_dB_all_values_length =
+ ARRAY_SIZE(set_dB_values);
+
+ cras_alsa_mixer_set_dBFS(c, target_dBFS, output_control);
+
+ /* Set volume should set playback dB on two of the coupled controls. */
+ EXPECT_EQ(2, snd_mixer_selem_set_playback_dB_all_called);
+ EXPECT_EQ(set_dB_values[0], expected_dB_value);
+ EXPECT_EQ(set_dB_values[1], expected_dB_value);
+
+ /* Mute should set playback switch on two of the coupled controls. */
+ cras_alsa_mixer_set_mute(c, 1, output_control);
+ EXPECT_EQ(2, snd_mixer_selem_set_playback_switch_all_called);
+ EXPECT_EQ(0, snd_mixer_selem_set_playback_switch_all_value);
+
+ /* Unmute should set playback switch on two of the coupled controls. */
+ cras_alsa_mixer_set_mute(c, 0, output_control);
+ EXPECT_EQ(4, snd_mixer_selem_set_playback_switch_all_called);
+ EXPECT_EQ(1, snd_mixer_selem_set_playback_switch_all_value);
+
+ EXPECT_EQ(max_volumes[0] - min_volumes[0],
+ cras_alsa_mixer_get_output_dB_range(output_control));
+
+ cras_alsa_mixer_destroy(c);
+ EXPECT_EQ(1, snd_mixer_close_called);
+ mixer_name_free(coupled_controls);
+}
+
+TEST(AlsaMixer, CoupledOutputHasMuteNoVolume) {
+ struct cras_alsa_mixer *c;
+ struct mixer_control *output_control;
+ struct mixer_control_element *c1, *c2, *c3, *c4;
+
+ static const long min_volumes[] = {-70};
+ static const long max_volumes[] = {30};
+
+ const char *coupled_output_names[] = {"Left Master",
+ "Right Master",
+ "Left Speaker",
+ "Right Speaker"};
+ struct mixer_name *coupled_controls =
+ mixer_name_add_array(NULL, coupled_output_names,
+ ARRAY_SIZE(coupled_output_names),
+ CRAS_STREAM_OUTPUT,
+ MIXER_NAME_VOLUME);
+ int element_playback_volume[] = {0, 0, 0, 0};
+ int element_playback_switches[] = {0, 0, 1, 1};
+
+ ResetStubData();
+
+ snd_mixer_find_elem_map[std::string("Left Master")] =
+ reinterpret_cast<snd_mixer_elem_t *>(1);
+ snd_mixer_find_elem_map[std::string("Right Master")] =
+ reinterpret_cast<snd_mixer_elem_t *>(2);
+ snd_mixer_find_elem_map[std::string("Left Speaker")] =
+ reinterpret_cast<snd_mixer_elem_t *>(3);
+ snd_mixer_find_elem_map[std::string("Right Speaker")] =
+ reinterpret_cast<snd_mixer_elem_t *>(4);
+
+ snd_mixer_selem_has_playback_volume_return_values = element_playback_volume;
+ snd_mixer_selem_has_playback_volume_return_values_length =
+ ARRAY_SIZE(element_playback_volume);
+ snd_mixer_selem_has_playback_switch_return_values = element_playback_switches;
+ snd_mixer_selem_has_playback_switch_return_values_length =
+ ARRAY_SIZE(element_playback_switches);
+
+ snd_mixer_selem_get_playback_dB_range_min_values = min_volumes;
+ snd_mixer_selem_get_playback_dB_range_max_values = max_volumes;
+ snd_mixer_selem_get_playback_dB_range_values_length = ARRAY_SIZE(min_volumes);
+
+ c = create_mixer_and_add_controls_by_name_matching(
+ "hw:0", NULL, coupled_controls);
+
+ ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
+ EXPECT_EQ(1, snd_mixer_open_called);
+ EXPECT_EQ(1, snd_mixer_attach_called);
+ EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
+ EXPECT_EQ(1, snd_mixer_selem_register_called);
+ EXPECT_EQ(1, snd_mixer_load_called);
+ EXPECT_EQ(0, snd_mixer_close_called);
+
+ output_control = c->output_controls;
+ EXPECT_EQ(NULL, output_control->next);
+ c1 = output_control->elements;
+ c2 = c1->next;
+ c3 = c2->next;
+ c4 = c3->next;
+ EXPECT_EQ(c1->elem, reinterpret_cast<snd_mixer_elem_t *>(1));
+ EXPECT_EQ(c2->elem, reinterpret_cast<snd_mixer_elem_t *>(2));
+ EXPECT_EQ(c3->elem, reinterpret_cast<snd_mixer_elem_t *>(3));
+ EXPECT_EQ(c4->elem, reinterpret_cast<snd_mixer_elem_t *>(4));
+ EXPECT_EQ(c4->next, reinterpret_cast<mixer_control_element *>(NULL));
+ EXPECT_EQ(c1->has_volume, 0);
+ EXPECT_EQ(c1->has_mute, 0);
+ EXPECT_EQ(c2->has_volume, 0);
+ EXPECT_EQ(c2->has_mute, 0);
+ EXPECT_EQ(c3->has_volume, 0);
+ EXPECT_EQ(c3->has_mute, 1);
+ EXPECT_EQ(c4->has_volume, 0);
+ EXPECT_EQ(c4->has_mute, 1);
+
+ EXPECT_EQ(0, cras_alsa_mixer_has_volume(output_control));
+ EXPECT_EQ(1, output_control->has_mute);
+
+ cras_alsa_mixer_destroy(c);
+ EXPECT_EQ(1, snd_mixer_close_called);
+ mixer_name_free(coupled_controls);
+}
+
+TEST(AlsaMixer, CoupledOutputHasVolumeNoMute) {
+ struct cras_alsa_mixer *c;
+ struct mixer_control *output_control;
+ struct mixer_control_element *c1, *c2, *c3, *c4;
+
+ static const long min_volumes[] = {-70, -70};
+ static const long max_volumes[] = {30, 30};
+
+ const char *coupled_output_names[] = {"Left Master",
+ "Right Master",
+ "Left Speaker",
+ "Right Speaker"};
+ struct mixer_name *coupled_controls =
+ mixer_name_add_array(NULL, coupled_output_names,
+ ARRAY_SIZE(coupled_output_names),
+ CRAS_STREAM_OUTPUT,
+ MIXER_NAME_VOLUME);
+ int element_playback_volume[] = {1, 1, 0, 0};
+ int element_playback_switches[] = {0, 0, 0, 0};
+
+ ResetStubData();
+
+ snd_mixer_find_elem_map[std::string("Left Master")] =
+ reinterpret_cast<snd_mixer_elem_t *>(1);
+ snd_mixer_find_elem_map[std::string("Right Master")] =
+ reinterpret_cast<snd_mixer_elem_t *>(2);
+ snd_mixer_find_elem_map[std::string("Left Speaker")] =
+ reinterpret_cast<snd_mixer_elem_t *>(3);
+ snd_mixer_find_elem_map[std::string("Right Speaker")] =
+ reinterpret_cast<snd_mixer_elem_t *>(4);
+
+ snd_mixer_selem_has_playback_volume_return_values = element_playback_volume;
+ snd_mixer_selem_has_playback_volume_return_values_length =
+ ARRAY_SIZE(element_playback_volume);
+ snd_mixer_selem_has_playback_switch_return_values = element_playback_switches;
+ snd_mixer_selem_has_playback_switch_return_values_length =
+ ARRAY_SIZE(element_playback_switches);
+
+ snd_mixer_selem_get_playback_dB_range_min_values = min_volumes;
+ snd_mixer_selem_get_playback_dB_range_max_values = max_volumes;
+ snd_mixer_selem_get_playback_dB_range_values_length = ARRAY_SIZE(min_volumes);
+
+ c = create_mixer_and_add_controls_by_name_matching(
+ "hw:0", NULL, coupled_controls);
+
+ ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
+ EXPECT_EQ(1, snd_mixer_open_called);
+ EXPECT_EQ(1, snd_mixer_attach_called);
+ EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
+ EXPECT_EQ(1, snd_mixer_selem_register_called);
+ EXPECT_EQ(1, snd_mixer_load_called);
+ EXPECT_EQ(0, snd_mixer_close_called);
+
+ output_control = c->output_controls;
+ EXPECT_EQ(NULL, output_control->next);
+ c1 = output_control->elements;
+ c2 = c1->next;
+ c3 = c2->next;
+ c4 = c3->next;
+ EXPECT_EQ(c1->elem, reinterpret_cast<snd_mixer_elem_t *>(1));
+ EXPECT_EQ(c2->elem, reinterpret_cast<snd_mixer_elem_t *>(2));
+ EXPECT_EQ(c3->elem, reinterpret_cast<snd_mixer_elem_t *>(3));
+ EXPECT_EQ(c4->elem, reinterpret_cast<snd_mixer_elem_t *>(4));
+ EXPECT_EQ(c4->next, reinterpret_cast<mixer_control_element *>(NULL));
+ EXPECT_EQ(c1->has_volume, 1);
+ EXPECT_EQ(c1->has_mute, 0);
+ EXPECT_EQ(c2->has_volume, 1);
+ EXPECT_EQ(c2->has_mute, 0);
+ EXPECT_EQ(c3->has_volume, 0);
+ EXPECT_EQ(c3->has_mute, 0);
+ EXPECT_EQ(c4->has_volume, 0);
+ EXPECT_EQ(c4->has_mute, 0);
+
+ EXPECT_EQ(1, cras_alsa_mixer_has_volume(output_control));
+ EXPECT_EQ(0, output_control->has_mute);
+
+ cras_alsa_mixer_destroy(c);
+ EXPECT_EQ(1, snd_mixer_close_called);
+ mixer_name_free(coupled_controls);
+}
+
+TEST(AlsaMixer, MixerName) {
+ struct mixer_name *names;
+ struct mixer_name *control;
+ size_t mixer_name_count;
+ static const char *element_names[] = {
+ "Master",
+ "PCM",
+ "Headphone",
+ "Speaker",
+ "HDMI",
+ "IEC958",
+ };
+
+ names = mixer_name_add_array(NULL, element_names,
+ ARRAY_SIZE(element_names),
+ CRAS_STREAM_OUTPUT, MIXER_NAME_VOLUME);
+ names = mixer_name_add(names, "Playback",
+ CRAS_STREAM_OUTPUT, MIXER_NAME_VOLUME);
+ names = mixer_name_add(names, "Main",
+ CRAS_STREAM_OUTPUT, MIXER_NAME_MAIN_VOLUME);
+ names = mixer_name_add(names, "Mic",
+ CRAS_STREAM_INPUT, MIXER_NAME_VOLUME);
+ names = mixer_name_add(names, "Capture",
+ CRAS_STREAM_INPUT, MIXER_NAME_MAIN_VOLUME);
+
+ /* Number of items (test mixer_name_add(_array)). */
+ mixer_name_count = 0;
+ DL_FOREACH(names, control) {
+ mixer_name_count++;
+ }
+ EXPECT_EQ(10, mixer_name_count);
+
+ /* Item not in the list: mismatch direction. */
+ control = mixer_name_find(names, "Main",
+ CRAS_STREAM_INPUT, MIXER_NAME_UNDEFINED);
+ EXPECT_EQ(1, control == NULL);
+
+ /* Item not in the list: mismatch type. */
+ control = mixer_name_find(names, "Main",
+ CRAS_STREAM_OUTPUT, MIXER_NAME_VOLUME);
+ EXPECT_EQ(1, control == NULL);
+
+ /* Find by name and direction. */
+ control = mixer_name_find(names, "Main",
+ CRAS_STREAM_OUTPUT, MIXER_NAME_UNDEFINED);
+ EXPECT_EQ(0, strcmp("Main", control->name));
+
+ /* Find by type and direction. */
+ control = mixer_name_find(names, NULL,
+ CRAS_STREAM_INPUT, MIXER_NAME_VOLUME);
+ EXPECT_EQ(0, strcmp("Mic", control->name));
+
+ mixer_name_free(names);
+}
+
+class AlsaMixerFullySpeced : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ callback_values_.clear();
+ callback_called_ = 0;
+ static snd_mixer_elem_t *elements[] = {
+ reinterpret_cast<snd_mixer_elem_t *>(1), // HP-L
+ reinterpret_cast<snd_mixer_elem_t *>(2), // HP-R
+ reinterpret_cast<snd_mixer_elem_t *>(3), // SPK-L
+ reinterpret_cast<snd_mixer_elem_t *>(4), // SPK-R
+ reinterpret_cast<snd_mixer_elem_t *>(5), // HDMI
+ reinterpret_cast<snd_mixer_elem_t *>(6), // CAPTURE
+ reinterpret_cast<snd_mixer_elem_t *>(7), // MIC-L
+ reinterpret_cast<snd_mixer_elem_t *>(8), // MIC-R
+ reinterpret_cast<snd_mixer_elem_t *>(0), // Unknown
+ };
+ static int element_playback_volume[] = {
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0, 0, 0,
+ };
+ static int element_playback_switches[] = {
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0, 0, 0,
+ };
+ static int element_capture_volume[] = {0, 0, 0, 0, 0,
+ 0,
+ 1,
+ 1,
+ };
+ static int element_capture_switches[] = {0, 0, 0, 0, 0,
+ 1,
+ 0,
+ 0,
+ };
+ static const long min_volumes[] = {-84, -84, -84, -84, -84, 0, 0, 0};
+ static const long max_volumes[] = {0, 0, 0, 0, 0, 0, 84, 84};
+ static const char *element_names[] = {
+ "HP-L",
+ "HP-R",
+ "SPK-L",
+ "SPK-R",
+ "HDMI",
+ "CAPTURE",
+ "MIC-L",
+ "MIC-R",
+ "Unknown"
+ };
+ struct ucm_section *sections = NULL;
+ struct ucm_section *section;
+ size_t i;
+
+ ResetStubData();
+
+ for (i = 0; i < ARRAY_SIZE(elements); i++)
+ snd_mixer_find_elem_map[element_names[i]] = elements[i];
+
+ section = ucm_section_create("NullElement", 0, CRAS_STREAM_OUTPUT,
+ NULL, NULL);
+ ucm_section_set_mixer_name(section, "Unknown");
+ DL_APPEND(sections, section);
+ section = ucm_section_create("Headphone", 0, CRAS_STREAM_OUTPUT,
+ "my-sound-card Headset Jack", "gpio");
+ ucm_section_add_coupled(section, "HP-L", MIXER_NAME_VOLUME);
+ ucm_section_add_coupled(section, "HP-R", MIXER_NAME_VOLUME);
+ DL_APPEND(sections, section);
+ section = ucm_section_create("Speaker", 0, CRAS_STREAM_OUTPUT,
+ NULL, NULL);
+ ucm_section_add_coupled(section, "SPK-L", MIXER_NAME_VOLUME);
+ ucm_section_add_coupled(section, "SPK-R", MIXER_NAME_VOLUME);
+ DL_APPEND(sections, section);
+ section = ucm_section_create("Mic", 0, CRAS_STREAM_INPUT,
+ "my-sound-card Headset Jack", "gpio");
+ ucm_section_set_mixer_name(section, "CAPTURE");
+ DL_APPEND(sections, section);
+ section = ucm_section_create("Internal Mic", 0, CRAS_STREAM_INPUT,
+ NULL, NULL);
+ ucm_section_add_coupled(section, "MIC-L", MIXER_NAME_VOLUME);
+ ucm_section_add_coupled(section, "MIC-R", MIXER_NAME_VOLUME);
+ DL_APPEND(sections, section);
+ section = ucm_section_create("HDMI", 0, CRAS_STREAM_OUTPUT,
+ NULL, NULL);
+ ucm_section_set_mixer_name(section, "HDMI");
+ DL_APPEND(sections, section);
+ ASSERT_NE(sections, (struct ucm_section *)NULL);
+
+ snd_mixer_selem_has_playback_volume_return_values =
+ element_playback_volume;
+ snd_mixer_selem_has_playback_volume_return_values_length =
+ ARRAY_SIZE(element_playback_volume);
+ snd_mixer_selem_has_playback_switch_return_values =
+ element_playback_switches;
+ snd_mixer_selem_has_playback_switch_return_values_length =
+ ARRAY_SIZE(element_playback_switches);
+ snd_mixer_selem_has_capture_volume_return_values =
+ element_capture_volume;
+ snd_mixer_selem_has_capture_volume_return_values_length =
+ ARRAY_SIZE(element_capture_volume);
+ snd_mixer_selem_has_capture_switch_return_values =
+ element_capture_switches;
+ snd_mixer_selem_has_capture_switch_return_values_length =
+ ARRAY_SIZE(element_capture_switches);
+ snd_mixer_selem_get_name_return_values = element_names;
+ snd_mixer_selem_get_name_return_values_length = ARRAY_SIZE(element_names);
+ snd_mixer_selem_get_capture_dB_range_min_values = min_volumes;
+ snd_mixer_selem_get_capture_dB_range_max_values = max_volumes;
+ snd_mixer_selem_get_capture_dB_range_values_length =
+ ARRAY_SIZE(min_volumes);
+
+ cras_mixer_ = cras_alsa_mixer_create("hw:0");
+ ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), cras_mixer_);
+ EXPECT_EQ(1, snd_mixer_open_called);
+ EXPECT_EQ(1, snd_mixer_attach_called);
+ EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
+ EXPECT_EQ(1, snd_mixer_selem_register_called);
+ EXPECT_EQ(1, snd_mixer_load_called);
+ EXPECT_EQ(0, snd_mixer_close_called);
+
+ section = sections;
+ EXPECT_EQ(-ENOENT,\
+ cras_alsa_mixer_add_controls_in_section(cras_mixer_, section));
+ ASSERT_NE((struct ucm_section *)NULL, section->next);
+ section = section->next;
+ EXPECT_EQ(0, cras_alsa_mixer_add_controls_in_section(cras_mixer_, section));
+ ASSERT_NE((struct ucm_section *)NULL, section->next);
+ section = section->next;
+ EXPECT_EQ(0, cras_alsa_mixer_add_controls_in_section(cras_mixer_, section));
+ ASSERT_NE((struct ucm_section *)NULL, section->next);
+ section = section->next;
+ EXPECT_EQ(0, cras_alsa_mixer_add_controls_in_section(cras_mixer_, section));
+ ASSERT_NE((struct ucm_section *)NULL, section->next);
+ section = section->next;
+ EXPECT_EQ(0, cras_alsa_mixer_add_controls_in_section(cras_mixer_, section));
+ ASSERT_NE((struct ucm_section *)NULL, section->next);
+ section = section->next;
+ EXPECT_EQ(0, cras_alsa_mixer_add_controls_in_section(cras_mixer_, section));
+ EXPECT_EQ(section->next, (struct ucm_section*)NULL);
+
+ EXPECT_EQ(9, snd_mixer_find_selem_called);
+ EXPECT_EQ(5, snd_mixer_selem_has_playback_volume_called);
+ EXPECT_EQ(5, snd_mixer_selem_has_playback_switch_called);
+ EXPECT_EQ(3, snd_mixer_selem_has_capture_volume_called);
+ EXPECT_EQ(3, snd_mixer_selem_has_capture_switch_called);
+ EXPECT_EQ(5, snd_mixer_selem_get_playback_dB_range_called);
+ EXPECT_EQ(2, snd_mixer_selem_get_capture_dB_range_called);
+
+ sections_ = sections;
+ }
+
+ virtual void TearDown() {
+ ucm_section_free_list(sections_);
+ cras_alsa_mixer_destroy(cras_mixer_);
+ EXPECT_EQ(1, snd_mixer_close_called);
+ }
+
+ static void Callback(struct mixer_control *control, void *arg) {
+ callback_called_++;
+ callback_values_.push_back(control);
+ }
+
+ struct cras_alsa_mixer *cras_mixer_;
+ static size_t callback_called_;
+ static std::vector<struct mixer_control *> callback_values_;
+ struct ucm_section *sections_;
+};
+
+size_t AlsaMixerFullySpeced::callback_called_;
+std::vector<struct mixer_control *> AlsaMixerFullySpeced::callback_values_;
+
+TEST_F(AlsaMixerFullySpeced, CheckControlCounts) {
+ cras_alsa_mixer_list_outputs(cras_mixer_,
+ AlsaMixerFullySpeced::Callback,
+ reinterpret_cast<void*>(555));
+ EXPECT_EQ(3, callback_called_);
+ callback_called_ = 0;
+ cras_alsa_mixer_list_inputs(cras_mixer_,
+ AlsaMixerFullySpeced::Callback,
+ reinterpret_cast<void*>(555));
+ EXPECT_EQ(2, callback_called_);
+}
+
+TEST_F(AlsaMixerFullySpeced, CheckFindOutputByNameNoMatch) {
+ struct mixer_control *out;
+
+ out = cras_alsa_mixer_get_output_matching_name(cras_mixer_,
+ "AAAAA Jack");
+ EXPECT_EQ(static_cast<struct mixer_control *>(NULL), out);
+}
+
+TEST_F(AlsaMixerFullySpeced, CheckFindOutputByName) {
+ struct mixer_control *out;
+
+ out = cras_alsa_mixer_get_output_matching_name(cras_mixer_,
+ "Headphone Jack");
+ EXPECT_NE(static_cast<struct mixer_control *>(NULL), out);
+}
+
+TEST_F(AlsaMixerFullySpeced, CheckFindControlForSection) {
+ struct mixer_control *control;
+ struct ucm_section *section = sections_;
+
+ // Look for the control for the Headphone section.
+ // We've already asserted that section != NULL above.
+ // Matching the control created by CoupledMixers.
+ section = section->next;
+ control = cras_alsa_mixer_get_control_for_section(cras_mixer_, section);
+ ASSERT_NE(static_cast<struct mixer_control *>(NULL), control);
+ EXPECT_EQ(0, strcmp(control->name, "Headphone"));
+
+ // Look for the control for the Mic section.
+ // Matching the control created by MixerName.
+ section = section->next->next;
+ control = cras_alsa_mixer_get_control_for_section(cras_mixer_, section);
+ ASSERT_NE(static_cast<struct mixer_control *>(NULL), control);
+ EXPECT_EQ(0, strcmp(control->name, "CAPTURE"));
+}
+
/* Stubs */
extern "C" {
@@ -1010,6 +1686,19 @@ int snd_mixer_selem_get_playback_dB_range(snd_mixer_elem_t *elem,
return 0;
}
+snd_mixer_elem_t *snd_mixer_find_selem(
+ snd_mixer_t *mixer, const snd_mixer_selem_id_t *id) {
+ std::string name(snd_mixer_selem_id_get_name(id));
+ unsigned int index = snd_mixer_selem_id_get_index(id);
+ snd_mixer_find_selem_called++;
+ if (index != 0)
+ return NULL;
+ if (snd_mixer_find_elem_map.find(name) == snd_mixer_find_elem_map.end()) {
+ return NULL;
+ }
+ return snd_mixer_find_elem_map[name];
+}
+
// From cras_volume_curve.
static long get_dBFS_default(const struct cras_volume_curve *curve,
size_t volume)
@@ -1017,6 +1706,15 @@ static long get_dBFS_default(const struct cras_volume_curve *curve,
return 100 * (volume - 100);
}
+struct cras_volume_curve *cras_volume_curve_create_default()
+{
+ struct cras_volume_curve *curve;
+ curve = (struct cras_volume_curve *)calloc(1, sizeof(*curve));
+ if (curve)
+ curve->get_dBFS = get_dBFS_default;
+ return curve;
+}
+
void cras_volume_curve_destroy(struct cras_volume_curve *curve)
{
cras_volume_curve_destroy_called++;
@@ -1030,7 +1728,6 @@ struct cras_volume_curve *cras_card_config_get_volume_curve_for_control(
{
struct cras_volume_curve *curve;
curve = (struct cras_volume_curve *)calloc(1, sizeof(*curve));
- cras_card_config_get_volume_curve_for_control_called++;
if (curve != NULL)
curve->get_dBFS = get_dBFS_default;
return curve;
@@ -1042,5 +1739,6 @@ struct cras_volume_curve *cras_card_config_get_volume_curve_for_control(
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
+ openlog(NULL, LOG_PERROR, LOG_USER);
return RUN_ALL_TESTS();
}
diff --git a/cras/src/tests/alsa_ucm_unittest.cc b/cras/src/tests/alsa_ucm_unittest.cc
index 212b31ad..822ef9eb 100644
--- a/cras/src/tests/alsa_ucm_unittest.cc
+++ b/cras/src/tests/alsa_ucm_unittest.cc
@@ -5,11 +5,15 @@
#include <gtest/gtest.h>
#include <iniparser.h>
#include <stdio.h>
+#include <syslog.h>
#include <map>
extern "C" {
#include "cras_alsa_ucm.h"
#include "cras_types.h"
+#include "cras_util.h"
+#include "utlist.h"
+#include "cras_util.h"
// Include C file to test static functions.
#include "cras_alsa_ucm.c"
@@ -23,7 +27,6 @@ static unsigned snd_use_case_mgr_open_called;
static unsigned snd_use_case_mgr_close_called;
static unsigned snd_use_case_get_called;
static std::vector<std::string> snd_use_case_get_id;
-static std::map<std::string, int> snd_use_case_get_ret_value;
static int snd_use_case_set_return;
static std::map<std::string, std::string> snd_use_case_get_value;
static unsigned snd_use_case_set_called;
@@ -31,6 +34,10 @@ static std::vector<std::pair<std::string, std::string> > snd_use_case_set_param;
static std::map<std::string, const char **> fake_list;
static std::map<std::string, unsigned> fake_list_size;
static unsigned snd_use_case_free_list_called;
+static std::vector<std::string> list_devices_callback_names;
+static std::vector<void*> list_devices_callback_args;
+static struct cras_use_case_mgr cras_ucm_mgr;
+static const char *avail_verbs[] = { "HiFi", "Comment for Verb1" };
static void ResetStubData() {
snd_use_case_mgr_open_called = 0;
@@ -43,9 +50,45 @@ static void ResetStubData() {
snd_use_case_free_list_called = 0;
snd_use_case_get_id.clear();
snd_use_case_get_value.clear();
- snd_use_case_get_ret_value.clear();
fake_list.clear();
fake_list_size.clear();
+ fake_list["_verbs"] = avail_verbs;
+ fake_list_size["_verbs"] = 2;
+ list_devices_callback_names.clear();
+ list_devices_callback_args.clear();
+ snd_use_case_mgr_open_mgr_ptr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ cras_ucm_mgr.use_case = CRAS_STREAM_TYPE_DEFAULT;
+}
+
+static void list_devices_callback(const char* section_name, void *arg) {
+ list_devices_callback_names.push_back(std::string(section_name));
+ list_devices_callback_args.push_back(arg);
+}
+
+static void SetSectionDeviceData() {
+ static const char *sections[] = { "Speaker", "Comment for Dev1",
+ "IntMic", "Comment for Dev2",
+ "Headphone", "Comment for Dev3",
+ "ExtMic", "Comment for Dev4",
+ "HDMI", "Comment for Dev5"};
+ fake_list["_devices/HiFi"] = sections;
+ fake_list_size["_devices/HiFi"] = 10;
+ std::string id_1 = "=PlaybackPCM/Speaker/HiFi";
+ std::string id_2 = "=CapturePCM/IntMic/HiFi";
+ std::string id_3 = "=PlaybackPCM/Headphone/HiFi";
+ std::string id_4 = "=CapturePCM/ExtMic/HiFi";
+ std::string id_5 = "=PlaybackPCM/HDMI/HiFi";
+ std::string value_1 = "test_card:0";
+ std::string value_2 = "test_card:0";
+ std::string value_3 = "test_card:0";
+ std::string value_4 = "test_card:0";
+ std::string value_5 = "test_card:1";
+
+ snd_use_case_get_value[id_1] = value_1;
+ snd_use_case_get_value[id_2] = value_2;
+ snd_use_case_get_value[id_3] = value_3;
+ snd_use_case_get_value[id_4] = value_4;
+ snd_use_case_get_value[id_5] = value_5;
}
TEST(AlsaUcm, CreateFailInvalidCard) {
@@ -71,13 +114,12 @@ TEST(AlsaUcm, CreateFailNoHiFi) {
}
TEST(AlsaUcm, CreateSuccess) {
- snd_use_case_mgr_t* mgr;
+ struct cras_use_case_mgr *mgr;
ResetStubData();
- snd_use_case_mgr_open_mgr_ptr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
mgr = ucm_create("foo");
- EXPECT_NE(static_cast<snd_use_case_mgr_t*>(NULL), mgr);
+ EXPECT_NE(static_cast<snd_use_case_mgr_t*>(NULL), mgr->mgr);
EXPECT_EQ(1, snd_use_case_mgr_open_called);
EXPECT_EQ(1, snd_use_case_set_called);
EXPECT_EQ(0, snd_use_case_mgr_close_called);
@@ -87,7 +129,7 @@ TEST(AlsaUcm, CreateSuccess) {
}
TEST(AlsaUcm, CheckEnabledEmptyList) {
- snd_use_case_mgr_t* mgr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
ResetStubData();
fake_list["_enadevs"] = NULL;
@@ -103,7 +145,7 @@ TEST(AlsaUcm, CheckEnabledEmptyList) {
}
TEST(AlsaUcm, CheckEnabledAlready) {
- snd_use_case_mgr_t* mgr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
const char *enabled[] = { "Dev2", "Dev1" };
ResetStubData();
@@ -121,7 +163,7 @@ TEST(AlsaUcm, CheckEnabledAlready) {
}
TEST(AlsaUcm, GetEdidForDev) {
- snd_use_case_mgr_t* mgr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
std::string id = "=EDIDFile/Dev1/HiFi";
std::string value = "EdidFileName";
const char *file_name;
@@ -129,7 +171,6 @@ TEST(AlsaUcm, GetEdidForDev) {
ResetStubData();
snd_use_case_get_value[id] = value;
- snd_use_case_get_ret_value[id] = 0;
file_name = ucm_get_edid_file_for_dev(mgr, "Dev1");
ASSERT_TRUE(file_name);
@@ -141,7 +182,7 @@ TEST(AlsaUcm, GetEdidForDev) {
}
TEST(AlsaUcm, GetCapControlForDev) {
- snd_use_case_mgr_t* mgr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
char *cap_control;
std::string id = "=CaptureControl/Dev1/HiFi";
std::string value = "MIC";
@@ -149,7 +190,6 @@ TEST(AlsaUcm, GetCapControlForDev) {
ResetStubData();
snd_use_case_get_value[id] = value;
- snd_use_case_get_ret_value[id] = 0;
cap_control = ucm_get_cap_control(mgr, "Dev1");
ASSERT_TRUE(cap_control);
@@ -161,7 +201,7 @@ TEST(AlsaUcm, GetCapControlForDev) {
}
TEST(AlsaUcm, GetOverrideType) {
- snd_use_case_mgr_t* mgr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
const char *override_type_name;
std::string id = "=OverrideNodeType/Dev1/HiFi";
std::string value = "HDMI";
@@ -169,7 +209,6 @@ TEST(AlsaUcm, GetOverrideType) {
ResetStubData();
snd_use_case_get_value[id] = value;
- snd_use_case_get_ret_value[id] = 0;
override_type_name = ucm_get_override_type_name(mgr, "Dev1");
ASSERT_TRUE(override_type_name);
@@ -180,40 +219,49 @@ TEST(AlsaUcm, GetOverrideType) {
EXPECT_EQ(snd_use_case_get_id[0], id);
}
-TEST(AlsaUcm, GetSectionForVar) {
- snd_use_case_mgr_t* mgr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
- const char *section_name;
+TEST(AlsaUcm, GetSectionsForVar) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ struct section_name *section_names, *c;
ResetStubData();
- const char *sections[] = { "Sec1", "Comment for Sec1", "Sec2",
- "Comment for Sec2" };
+ const char *sections[] = { "Sec1", "Comment for Sec1",
+ "Sec2", "Comment for Sec2",
+ "Sec3", "Comment for Sec3"};
fake_list["Identifier"] = sections;
- fake_list_size["Identifier"] = 4;
+ fake_list_size["Identifier"] = 6;
std::string id_1 = "=Var/Sec1/HiFi";
std::string id_2 = "=Var/Sec2/HiFi";
+ std::string id_3 = "=Var/Sec3/HiFi";
std::string value_1 = "Value1";
std::string value_2 = "Value2";
+ std::string value_3 = "Value2";
- snd_use_case_get_ret_value[id_1] = 0;
snd_use_case_get_value[id_1] = value_1;
- snd_use_case_get_ret_value[id_2] = 0;
snd_use_case_get_value[id_2] = value_2;
+ snd_use_case_get_value[id_3] = value_3;
- section_name = ucm_get_section_for_var(mgr, "Var", "Value2", "Identifier",
- CRAS_STREAM_OUTPUT);
+ section_names = ucm_get_sections_for_var(mgr, "Var", "Value2", "Identifier",
+ CRAS_STREAM_OUTPUT);
- ASSERT_TRUE(section_name);
- EXPECT_EQ(0, strcmp(section_name, "Sec2"));
- free((void*)section_name);
+ ASSERT_TRUE(section_names);
+ EXPECT_EQ(0, strcmp(section_names->name, "Sec2"));
+ EXPECT_EQ(0, strcmp(section_names->next->name, "Sec3"));
- ASSERT_EQ(2, snd_use_case_get_called);
+ ASSERT_EQ(3, snd_use_case_get_called);
EXPECT_EQ(snd_use_case_get_id[0], id_1);
EXPECT_EQ(snd_use_case_get_id[1], id_2);
+ EXPECT_EQ(snd_use_case_get_id[2], id_3);
+
+ DL_FOREACH(section_names, c) {
+ DL_DELETE(section_names, c);
+ free((void*)c->name);
+ free(c);
+ }
}
TEST(AlsaUcm, GetDevForJack) {
- snd_use_case_mgr_t* mgr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
const char *dev_name;
const char *devices[] = { "Dev1", "Comment for Dev1", "Dev2",
"Comment for Dev2" };
@@ -227,9 +275,7 @@ TEST(AlsaUcm, GetDevForJack) {
std::string value_1 = "Value1";
std::string value_2 = "Value2";
- snd_use_case_get_ret_value[id_1] = 0;
snd_use_case_get_value[id_1] = value_1;
- snd_use_case_get_ret_value[id_2] = 0;
snd_use_case_get_value[id_2] = value_2;
dev_name = ucm_get_dev_for_jack(mgr, value_2.c_str(), CRAS_STREAM_OUTPUT);
ASSERT_TRUE(dev_name);
@@ -241,8 +287,90 @@ TEST(AlsaUcm, GetDevForJack) {
EXPECT_EQ(snd_use_case_get_id[1], id_2);
}
+TEST(AlsaUcm, GetDevForHeadphoneJack) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ const char *dev_name;
+ const char *devices[] = { "Mic", "Comment for Dev1", "Headphone",
+ "Comment for Dev2" };
+
+ ResetStubData();
+
+ fake_list["_devices/HiFi"] = devices;
+ fake_list_size["_devices/HiFi"] = 4;
+ std::string id_1 = "=JackName/Mic/HiFi";
+ std::string id_2 = "=JackName/Headphone/HiFi";
+ std::string value = "JackValue";
+
+ snd_use_case_get_value[id_1] = value;
+ snd_use_case_get_value[id_2] = value;
+
+ /* Looking for jack with matched value with output direction, Headphone will
+ * be found even though Mic section has the matched value too. */
+ dev_name = ucm_get_dev_for_jack(mgr, value.c_str(), CRAS_STREAM_OUTPUT);
+
+ ASSERT_TRUE(dev_name);
+ EXPECT_EQ(0, strcmp(dev_name, "Headphone"));
+ free((void*)dev_name);
+}
+
+TEST(AlsaUcm, GetDevForMicJack) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ const char *dev_name;
+ const char *devices[] = { "Headphone", "Comment for Dev1", "Mic",
+ "Comment for Dev2" };
+
+ ResetStubData();
+
+ fake_list["_devices/HiFi"] = devices;
+ fake_list_size["_devices/HiFi"] = 4;
+ std::string id_1 = "=JackName/Headphone/HiFi";
+ std::string id_2 = "=JackName/Mic/HiFi";
+ std::string value = "JackValue";
+
+ snd_use_case_get_value[id_1] = value;
+ snd_use_case_get_value[id_2] = value;
+
+ /* Looking for jack with matched value with input direction, Mic will be found
+ * even though Headphone section has the matched value too. */
+ dev_name = ucm_get_dev_for_jack(mgr, value.c_str(), CRAS_STREAM_INPUT);
+
+ ASSERT_TRUE(dev_name);
+ EXPECT_EQ(0, strcmp(dev_name, "Mic"));
+ free((void*)dev_name);
+}
+
+TEST(AlsaUcm, GetDevForMixer) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ const char *dev_name_out, *dev_name_in;
+ const char *devices[] = { "Dev1", "Comment for Dev1", "Dev2",
+ "Comment for Dev2" };
+
+ ResetStubData();
+
+ fake_list["_devices/HiFi"] = devices;
+ fake_list_size["_devices/HiFi"] = 4;
+ std::string id_1 = "=MixerName/Dev1/HiFi";
+ std::string id_2 = "=MixerName/Dev2/HiFi";
+ std::string value_1 = "Value1";
+ std::string value_2 = "Value2";
+
+ snd_use_case_get_value[id_1] = value_1;
+ snd_use_case_get_value[id_2] = value_2;
+ dev_name_out = ucm_get_dev_for_mixer(
+ mgr, value_1.c_str(), CRAS_STREAM_OUTPUT);
+ dev_name_in = ucm_get_dev_for_mixer(mgr, value_2.c_str(), CRAS_STREAM_INPUT);
+
+ ASSERT_TRUE(dev_name_out);
+ EXPECT_EQ(0, strcmp(dev_name_out, "Dev1"));
+ free((void*)dev_name_out);
+
+ ASSERT_TRUE(dev_name_in);
+ EXPECT_EQ(0, strcmp(dev_name_in, "Dev2"));
+ free((void*)dev_name_in);
+}
+
TEST(AlsaUcm, GetDeviceNameForDevice) {
- snd_use_case_mgr_t* mgr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
const char *input_dev_name, *output_dev_name;
const char *devices[] = { "Dev1", "Comment for Dev1", "Dev2",
"Comment for Dev2" };
@@ -256,9 +384,7 @@ TEST(AlsaUcm, GetDeviceNameForDevice) {
std::string value_1 = "DeviceName1";
std::string value_2 = "DeviceName2";
- snd_use_case_get_ret_value[id_1] = 0;
snd_use_case_get_value[id_1] = value_1;
- snd_use_case_get_ret_value[id_2] = 0;
snd_use_case_get_value[id_2] = value_2;
input_dev_name = ucm_get_device_name_for_dev(mgr, "Dev1", CRAS_STREAM_INPUT);
output_dev_name = ucm_get_device_name_for_dev(mgr, "Dev2", CRAS_STREAM_OUTPUT);
@@ -272,8 +398,117 @@ TEST(AlsaUcm, GetDeviceNameForDevice) {
EXPECT_EQ(snd_use_case_get_id[1], id_2);
}
+TEST(AlsaUcm, GetDeviceRateForDevice) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ int input_dev_rate, output_dev_rate;
+ const char *devices[] = { "Dev1", "Comment for Dev1", "Dev2",
+ "Comment for Dev2" };
+
+ ResetStubData();
+
+ fake_list["_devices/HiFi"] = devices;
+ fake_list_size["_devices/HiFi"] = 4;
+ std::string id_1 = "=CaptureRate/Dev1/HiFi";
+ std::string id_2 = "=PlaybackRate/Dev2/HiFi";
+ std::string value_1 = "44100";
+ std::string value_2 = "48000";
+
+ snd_use_case_get_value[id_1] = value_1;
+ snd_use_case_get_value[id_2] = value_2;
+ input_dev_rate = ucm_get_sample_rate_for_dev(mgr, "Dev1", CRAS_STREAM_INPUT);
+ output_dev_rate = ucm_get_sample_rate_for_dev(mgr, "Dev2",
+ CRAS_STREAM_OUTPUT);
+ EXPECT_EQ(44100, input_dev_rate);
+ EXPECT_EQ(48000, output_dev_rate);
+
+ ASSERT_EQ(2, snd_use_case_get_called);
+ EXPECT_EQ(snd_use_case_get_id[0], id_1);
+ EXPECT_EQ(snd_use_case_get_id[1], id_2);
+}
+
+TEST(AlsaUcm, GetCaptureChannelMapForDevice) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ int8_t channel_layout[CRAS_CH_MAX];
+ int rc;
+
+ ResetStubData();
+
+ std::string id_1 = "=CaptureChannelMap/Dev1/HiFi";
+ std::string value_1 = "-1 -1 0 1 -1 -1 -1 -1 -1 -1 -1";
+
+ snd_use_case_get_value[id_1] = value_1;
+ rc = ucm_get_capture_chmap_for_dev(mgr, "Dev1", channel_layout);
+
+ EXPECT_EQ(0, rc);
+
+ ASSERT_EQ(1, snd_use_case_get_called);
+ EXPECT_EQ(snd_use_case_get_id[0], id_1);
+ EXPECT_EQ(channel_layout[0], -1);
+ EXPECT_EQ(channel_layout[1], -1);
+ EXPECT_EQ(channel_layout[2], 0);
+ EXPECT_EQ(channel_layout[3], 1);
+ EXPECT_EQ(channel_layout[4], -1);
+ EXPECT_EQ(channel_layout[5], -1);
+ EXPECT_EQ(channel_layout[6], -1);
+ EXPECT_EQ(channel_layout[7], -1);
+ EXPECT_EQ(channel_layout[8], -1);
+ EXPECT_EQ(channel_layout[9], -1);
+ EXPECT_EQ(channel_layout[10], -1);
+}
+
+TEST(AlsaUcm, GetHotwordModels) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ const char *models;
+ const char *modifiers[] = { "Mod1",
+ "Comment1",
+ "Hotword Model en",
+ "Comment2",
+ "Hotword Model jp",
+ "Comment3",
+ "Mod2",
+ "Comment4",
+ "Hotword Model de",
+ "Comment5" };
+ ResetStubData();
+
+ fake_list["_modifiers/HiFi"] = modifiers;
+ fake_list_size["_modifiers/HiFi"] = 10;
+
+ models = ucm_get_hotword_models(mgr);
+ ASSERT_TRUE(models);
+ EXPECT_EQ(0, strcmp(models, "en,jp,de"));
+}
+
+TEST(AlsaUcm, SetHotwordModel) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ const char *modifiers[] = { "Hotword Model en",
+ "Comment1",
+ "Hotword Model jp",
+ "Comment2",
+ "Hotword Model de",
+ "Comment3" };
+ const char *enabled_mods[] = { "Hotword Model en" };
+ ResetStubData();
+
+ fake_list["_modifiers/HiFi"] = modifiers;
+ fake_list_size["_modifiers/HiFi"] = 6;
+
+ EXPECT_EQ(-EINVAL, ucm_set_hotword_model(mgr, "zh"));
+ EXPECT_EQ(0, snd_use_case_set_called);
+
+ fake_list["_enamods"] = enabled_mods;
+ fake_list_size["_enamods"] = 1;
+ ucm_set_hotword_model(mgr, "jp");
+
+ EXPECT_EQ(2, snd_use_case_set_called);
+ EXPECT_EQ(snd_use_case_set_param[0],
+ std::make_pair(std::string("_dismod"), std::string("Hotword Model en")));
+ EXPECT_EQ(snd_use_case_set_param[1],
+ std::make_pair(std::string("_enamod"), std::string("Hotword Model jp")));
+}
+
TEST(AlsaUcm, SwapModeExists) {
- snd_use_case_mgr_t* mgr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
int rc;
const char *modifiers_1[] = { "Speaker Swap Mode",
"Comment for Speaker Swap Mode",
@@ -298,7 +533,7 @@ TEST(AlsaUcm, SwapModeExists) {
}
TEST(AlsaUcm, EnableSwapMode) {
- snd_use_case_mgr_t* mgr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
int rc;
const char *modifiers[] = { "Speaker Swap Mode",
"Comment for Speaker Swap Mode",
@@ -330,7 +565,7 @@ TEST(AlsaUcm, EnableSwapMode) {
}
TEST(AlsaUcm, DisableSwapMode) {
- snd_use_case_mgr_t* mgr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
int rc;
const char *modifiers[] = { "Speaker Swap Mode",
"Comment for Speaker Swap Mode",
@@ -363,7 +598,7 @@ TEST(AlsaUcm, DisableSwapMode) {
}
TEST(AlsaFlag, GetFlag) {
- snd_use_case_mgr_t* mgr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
char *flag_value;
std::string id = "=FlagName//HiFi";
@@ -382,7 +617,7 @@ TEST(AlsaFlag, GetFlag) {
}
TEST(AlsaUcm, ModifierEnabled) {
- snd_use_case_mgr_t* mgr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
int enabled;
ResetStubData();
@@ -400,7 +635,7 @@ TEST(AlsaUcm, ModifierEnabled) {
}
TEST(AlsaUcm, SetModifierEnabled) {
- snd_use_case_mgr_t* mgr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
ResetStubData();
@@ -421,7 +656,7 @@ TEST(AlsaUcm, EndWithSuffix) {
}
TEST(AlsaUcm, SectionExistsWithName) {
- snd_use_case_mgr_t* mgr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
const char *sections[] = { "Sec1", "Comment for Sec1", "Sec2",
"Comment for Sec2" };
@@ -435,7 +670,7 @@ TEST(AlsaUcm, SectionExistsWithName) {
}
TEST(AlsaUcm, SectionExistsWithSuffix) {
- snd_use_case_mgr_t* mgr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
ResetStubData();
@@ -449,7 +684,7 @@ TEST(AlsaUcm, SectionExistsWithSuffix) {
}
TEST(AlsaUcm, DisableSoftwareVolume) {
- snd_use_case_mgr_t* mgr = reinterpret_cast<snd_use_case_mgr_t*>(0x55);
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
unsigned int disable_software_volume;
std::string id = "=DisableSoftwareVolume//HiFi";
std::string value = "1";
@@ -457,7 +692,6 @@ TEST(AlsaUcm, DisableSoftwareVolume) {
ResetStubData();
snd_use_case_get_value[id] = value;
- snd_use_case_get_ret_value[id] = 0;
disable_software_volume = ucm_get_disable_software_volume(mgr);
ASSERT_TRUE(disable_software_volume);
@@ -466,6 +700,697 @@ TEST(AlsaUcm, DisableSoftwareVolume) {
EXPECT_EQ(snd_use_case_get_id[0], id);
}
+TEST(AlsaUcm, GetCoupledMixersForDevice) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ struct mixer_name *mixer_names_1, *mixer_names_2, *c;
+ const char *devices[] = { "Dev1", "Comment for Dev1", "Dev2",
+ "Comment for Dev2" };
+
+ ResetStubData();
+
+ fake_list["_devices/HiFi"] = devices;
+ fake_list_size["_devices/HiFi"] = 4;
+ std::string id_1 = "=CoupledMixers/Dev1/HiFi";
+ std::string value_1 = "Mixer Name1,Mixer Name2,Mixer Name3";
+ std::string id_2 = "=CoupledMixers/Dev2/HiFi";
+ std::string value_2 = "";
+ snd_use_case_get_value[id_1] = value_1;
+ snd_use_case_get_value[id_2] = value_2;
+ mixer_names_1 = ucm_get_coupled_mixer_names(mgr, "Dev1");
+ mixer_names_2 = ucm_get_coupled_mixer_names(mgr, "Dev2");
+
+ ASSERT_TRUE(mixer_names_1);
+ EXPECT_EQ(0, strcmp(mixer_names_1->name, "Mixer Name1"));
+ EXPECT_EQ(0, strcmp(mixer_names_1->next->name, "Mixer Name2"));
+ EXPECT_EQ(0, strcmp(mixer_names_1->next->next->name, "Mixer Name3"));
+ EXPECT_EQ(NULL, mixer_names_1->next->next->next);
+
+ EXPECT_EQ(NULL, mixer_names_2);
+
+ DL_FOREACH(mixer_names_1, c) {
+ DL_DELETE(mixer_names_1, c);
+ free((void*)c->name);
+ free(c);
+ }
+}
+
+TEST(AlsaUcm, FreeMixerNames) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ struct mixer_name *mixer_names_1;
+ const char *devices[] = { "Dev1", "Comment for Dev1"};
+
+ ResetStubData();
+
+ fake_list["_devices/HiFi"] = devices;
+ fake_list_size["_devices/HiFi"] = 2;
+ std::string id_1 = "=CoupledMixers/Dev1/HiFi";
+ std::string value_1 = "Mixer Name1,Mixer Name2,Mixer Name3";
+ snd_use_case_get_value[id_1] = value_1;
+ mixer_names_1 = ucm_get_coupled_mixer_names(mgr, "Dev1");
+
+
+ ASSERT_TRUE(mixer_names_1);
+ EXPECT_EQ(0, strcmp(mixer_names_1->name, "Mixer Name1"));
+ EXPECT_EQ(0, strcmp(mixer_names_1->next->name, "Mixer Name2"));
+ EXPECT_EQ(0, strcmp(mixer_names_1->next->next->name, "Mixer Name3"));
+ EXPECT_EQ(NULL, mixer_names_1->next->next->next);
+
+ /* No way to actually check if memory is freed. */
+ mixer_name_free(mixer_names_1);
+}
+
+TEST(AlsaUcm, MaxSoftwareGain) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ long max_software_gain;
+ int ret;
+ std::string id = "=MaxSoftwareGain/Internal Mic/HiFi";
+ std::string value = "2000";
+
+ ResetStubData();
+
+ /* Value can be found in UCM. */
+ snd_use_case_get_value[id] = value;
+
+ ret = ucm_get_max_software_gain(mgr, "Internal Mic", &max_software_gain);
+
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(2000, max_software_gain);
+
+ ResetStubData();
+
+ /* Value can not be found in UCM. */
+ ret = ucm_get_max_software_gain(mgr, "Internal Mic", &max_software_gain);
+
+ ASSERT_TRUE(ret);
+}
+
+TEST(AlsaUcm, DefaultNodeGain) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ long default_node_gain;
+ int ret;
+ std::string id = "=DefaultNodeGain/Internal Mic/HiFi";
+ std::string value = "-2000";
+
+ ResetStubData();
+
+ /* Value can be found in UCM. */
+ snd_use_case_get_value[id] = value;
+
+ ret = ucm_get_default_node_gain(mgr, "Internal Mic", &default_node_gain);
+
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(-2000, default_node_gain);
+
+ ResetStubData();
+
+ /* Value can not be found in UCM. */
+ ret = ucm_get_default_node_gain(mgr, "Internal Mic", &default_node_gain);
+
+ ASSERT_TRUE(ret);
+}
+
+TEST(AlsaUcm, UseFullySpecifiedUCMConfig) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ int fully_specified_flag;
+
+ std::string id = "=FullySpecifiedUCM//HiFi";
+ ResetStubData();
+
+ /* Flag is not set */
+ fully_specified_flag = ucm_has_fully_specified_ucm_flag(mgr);
+ ASSERT_FALSE(fully_specified_flag);
+
+ /* Flag is set to "1". */
+ snd_use_case_get_value[id] = std::string("1");
+ fully_specified_flag = ucm_has_fully_specified_ucm_flag(mgr);
+ ASSERT_TRUE(fully_specified_flag);
+
+ /* Flag is set to "0". */
+ snd_use_case_get_value[id] = std::string("0");
+ fully_specified_flag = ucm_has_fully_specified_ucm_flag(mgr);
+ ASSERT_FALSE(fully_specified_flag);
+}
+
+TEST(AlsaUcm, EnableHtimestampFlag) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ unsigned int enable_htimestamp_flag;
+
+ std::string id = "=EnableHtimestamp//HiFi";
+ ResetStubData();
+
+ /* Flag is not set */
+ enable_htimestamp_flag = ucm_get_enable_htimestamp_flag(mgr);
+ ASSERT_FALSE(enable_htimestamp_flag);
+
+ /* Flag is set to "1". */
+ snd_use_case_get_value[id] = std::string("1");
+ enable_htimestamp_flag = ucm_get_enable_htimestamp_flag(mgr);
+ ASSERT_TRUE(enable_htimestamp_flag);
+
+ /* Flag is set to "0". */
+ snd_use_case_get_value[id] = std::string("0");
+ enable_htimestamp_flag = ucm_get_enable_htimestamp_flag(mgr);
+ ASSERT_FALSE(enable_htimestamp_flag);
+}
+
+TEST(AlsaUcm, GetMixerNameForDevice) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ const char *mixer_name_1, *mixer_name_2;
+ const char *devices[] = { "Dev1", "Comment for Dev1", "Dev2",
+ "Comment for Dev2" };
+
+ ResetStubData();
+
+ fake_list["_devices/HiFi"] = devices;
+ fake_list_size["_devices/HiFi"] = 4;
+ std::string id_1 = "=MixerName/Dev1/HiFi";
+ std::string id_2 = "=MixerName/Dev2/HiFi";
+ std::string value_1 = "MixerName1";
+ std::string value_2 = "MixerName2";
+
+ snd_use_case_get_value[id_1] = value_1;
+ snd_use_case_get_value[id_2] = value_2;
+ mixer_name_1 = ucm_get_mixer_name_for_dev(mgr, "Dev1");
+ mixer_name_2 = ucm_get_mixer_name_for_dev(mgr, "Dev2");
+
+ EXPECT_EQ(0, strcmp(mixer_name_1, value_1.c_str()));
+ EXPECT_EQ(0, strcmp(mixer_name_2, value_2.c_str()));
+}
+
+TEST(AlsaUcm, GetMainVolumeMixerName) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ struct mixer_name *mixer_names_1, *mixer_names_2, *c;
+
+ ResetStubData();
+
+ std::string id = "=MainVolumeNames//HiFi";
+ std::string value_1 = "Mixer Name1,Mixer Name2,Mixer Name3";
+
+ snd_use_case_get_value[id] = value_1;
+ mixer_names_1 = ucm_get_main_volume_names(mgr);
+
+ ResetStubData();
+
+ /* Can not find MainVolumeNames */
+ mixer_names_2 = ucm_get_main_volume_names(mgr);
+
+ ASSERT_TRUE(mixer_names_1);
+ EXPECT_EQ(0, strcmp(mixer_names_1->name, "Mixer Name1"));
+ EXPECT_EQ(0, strcmp(mixer_names_1->next->name, "Mixer Name2"));
+ EXPECT_EQ(0, strcmp(mixer_names_1->next->next->name, "Mixer Name3"));
+ EXPECT_EQ(NULL, mixer_names_1->next->next->next);
+
+ DL_FOREACH(mixer_names_1, c) {
+ DL_DELETE(mixer_names_1, c);
+ free((void*)c->name);
+ free(c);
+ }
+
+ EXPECT_EQ(NULL, mixer_names_2);
+}
+
+TEST(AlsaUcm, ListSectionsByDeviceNameOutput) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ void* callback_arg = reinterpret_cast<void*>(0x56);
+ int listed = 0;
+
+ ResetStubData();
+ SetSectionDeviceData();
+
+ listed = ucm_list_section_devices_by_device_name(
+ mgr, CRAS_STREAM_OUTPUT, "test_card:0", list_devices_callback,
+ callback_arg);
+
+ EXPECT_EQ(2, listed);
+ EXPECT_EQ(2, list_devices_callback_names.size());
+ EXPECT_EQ(2, list_devices_callback_args.size());
+
+ EXPECT_EQ(
+ 0, strcmp(list_devices_callback_names[0].c_str(), "Speaker"));
+ EXPECT_EQ(callback_arg, list_devices_callback_args[0]);
+
+ EXPECT_EQ(
+ 0, strcmp(list_devices_callback_names[1].c_str(), "Headphone"));
+ EXPECT_EQ(callback_arg, list_devices_callback_args[1]);
+}
+
+TEST(AlsaUcm, ListSectionsByDeviceNameInput) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ void* callback_arg = reinterpret_cast<void*>(0x56);
+ int listed = 0;
+
+ ResetStubData();
+ SetSectionDeviceData();
+
+ listed = ucm_list_section_devices_by_device_name(
+ mgr, CRAS_STREAM_INPUT, "test_card:0", list_devices_callback,
+ callback_arg);
+
+ EXPECT_EQ(2, listed);
+ EXPECT_EQ(2, list_devices_callback_names.size());
+ EXPECT_EQ(2, list_devices_callback_args.size());
+
+ EXPECT_EQ(
+ 0, strcmp(list_devices_callback_names[0].c_str(), "IntMic"));
+ EXPECT_EQ(callback_arg, list_devices_callback_args[0]);
+
+ EXPECT_EQ(
+ 0, strcmp(list_devices_callback_names[1].c_str(), "ExtMic"));
+ EXPECT_EQ(callback_arg, list_devices_callback_args[1]);
+}
+
+TEST(AlsaUcm, GetJackNameForDevice) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ const char *jack_name_1, *jack_name_2;
+ const char *devices[] = { "Dev1", "Comment for Dev1", "Dev2",
+ "Comment for Dev2" };
+
+ ResetStubData();
+
+ fake_list["_devices/HiFi"] = devices;
+ fake_list_size["_devices/HiFi"] = 4;
+ std::string id_1 = "=JackName/Dev1/HiFi";
+ std::string value_1 = "JackName1";
+
+ snd_use_case_get_value[id_1] = value_1;
+ jack_name_1 = ucm_get_jack_name_for_dev(mgr, "Dev1");
+ jack_name_2 = ucm_get_jack_name_for_dev(mgr, "Dev2");
+
+ EXPECT_EQ(0, strcmp(jack_name_1, value_1.c_str()));
+ EXPECT_EQ(NULL, jack_name_2);
+}
+
+TEST(AlsaUcm, GetJackTypeForDevice) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ const char *jack_type_1, *jack_type_2, *jack_type_3, *jack_type_4;
+ const char *devices[] = { "Dev1", "Comment for Dev1",
+ "Dev2", "Comment for Dev2",
+ "Dev3", "Comment for Dev3",
+ "Dev4", "Comment for Dev4"};
+
+ ResetStubData();
+
+ fake_list["_devices/HiFi"] = devices;
+ fake_list_size["_devices/HiFi"] = 8;
+ std::string id_1 = "=JackType/Dev1/HiFi";
+ std::string value_1 = "hctl";
+ std::string id_2 = "=JackType/Dev2/HiFi";
+ std::string value_2 = "gpio";
+ std::string id_3 = "=JackType/Dev3/HiFi";
+ std::string value_3 = "something";
+
+ snd_use_case_get_value[id_1] = value_1;
+ snd_use_case_get_value[id_2] = value_2;
+ snd_use_case_get_value[id_3] = value_3;
+
+ jack_type_1 = ucm_get_jack_type_for_dev(mgr, "Dev1");
+ jack_type_2 = ucm_get_jack_type_for_dev(mgr, "Dev2");
+ jack_type_3 = ucm_get_jack_type_for_dev(mgr, "Dev3");
+ jack_type_4 = ucm_get_jack_type_for_dev(mgr, "Dev4");
+
+ /* Only "hctl" and "gpio" are valid types. */
+ EXPECT_EQ(0, strcmp(jack_type_1, value_1.c_str()));
+ EXPECT_EQ(0, strcmp(jack_type_2, value_2.c_str()));
+ EXPECT_EQ(NULL, jack_type_3);
+ EXPECT_EQ(NULL, jack_type_4);
+}
+
+TEST(AlsaUcm, GetPeriodFramesForDevice) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ int dma_period_1, dma_period_2, dma_period_3;
+ const char *devices[] = { "Dev1", "Comment for Dev1",
+ "Dev2", "Comment for Dev2",
+ "Dev3", "Comment for Dev3" };
+
+ ResetStubData();
+
+ fake_list["_devices/HiFi"] = devices;
+ fake_list_size["_devices/HiFi"] = 6;
+ std::string id_1 = "=DmaPeriodMicrosecs/Dev1/HiFi";
+ std::string value_1 = "1000";
+ std::string id_2 = "=DmaPeriodMicrosecs/Dev2/HiFi";
+ std::string value_2 = "-10";
+
+ snd_use_case_get_value[id_1] = value_1;
+ snd_use_case_get_value[id_2] = value_2;
+
+ dma_period_1 = ucm_get_dma_period_for_dev(mgr, "Dev1");
+ dma_period_2 = ucm_get_dma_period_for_dev(mgr, "Dev2");
+ dma_period_3 = ucm_get_dma_period_for_dev(mgr, "Dev3");
+
+ /* Only "hctl" and "gpio" are valid types. */
+ EXPECT_EQ(1000, dma_period_1);
+ EXPECT_EQ(0, dma_period_2);
+ EXPECT_EQ(0, dma_period_3);
+}
+
+TEST(AlsaUcm, UcmSection) {
+ struct ucm_section *section_list = NULL;
+ struct ucm_section *section;
+ struct mixer_name *controls = NULL;
+ struct mixer_name *m_name;
+ int dev_idx = 0;
+ size_t i;
+ enum CRAS_STREAM_DIRECTION dir = CRAS_STREAM_OUTPUT;
+ static const char *name = "Headphone";
+ static const char *jack_name = "my-card-name Headset Jack";
+ static const char *jack_type = "gpio";
+ static const char *mixer_name = "Control1";
+ static const char *coupled_names[] = {
+ "Coupled1",
+ "Coupled2"
+ };
+
+ section = ucm_section_create(NULL, 0, CRAS_STREAM_OUTPUT, NULL, NULL);
+ EXPECT_EQ(reinterpret_cast<struct ucm_section*>(NULL), section);
+
+ section = ucm_section_create(name, dev_idx, dir, jack_name, jack_type);
+ EXPECT_NE(reinterpret_cast<struct ucm_section*>(NULL), section);
+ EXPECT_NE(name, section->name);
+ EXPECT_EQ(0, strcmp(name, section->name));
+ EXPECT_EQ(dev_idx, section->dev_idx);
+ EXPECT_EQ(dir, section->dir);
+ EXPECT_NE(jack_name, section->jack_name);
+ EXPECT_NE(jack_type, section->jack_type);
+ EXPECT_EQ(section->prev, section);
+ EXPECT_EQ(reinterpret_cast<const char *>(NULL), section->mixer_name);
+ EXPECT_EQ(reinterpret_cast<struct mixer_name*>(NULL), section->coupled);
+
+ EXPECT_EQ(-EINVAL, ucm_section_set_mixer_name(section, NULL));
+ EXPECT_EQ(-EINVAL, ucm_section_set_mixer_name(NULL, mixer_name));
+ EXPECT_EQ(0, ucm_section_set_mixer_name(section, mixer_name));
+
+ EXPECT_NE(section->mixer_name, mixer_name);
+ EXPECT_EQ(0, strcmp(section->mixer_name, mixer_name));
+
+ EXPECT_EQ(-EINVAL, ucm_section_add_coupled(
+ section, NULL, MIXER_NAME_VOLUME));
+ EXPECT_EQ(-EINVAL, ucm_section_add_coupled(
+ NULL, coupled_names[0], MIXER_NAME_VOLUME));
+ EXPECT_EQ(0, ucm_section_add_coupled(
+ section, coupled_names[0], MIXER_NAME_VOLUME));
+
+ EXPECT_EQ(-EINVAL, ucm_section_concat_coupled(section, NULL));
+ EXPECT_EQ(-EINVAL, ucm_section_concat_coupled(
+ NULL, reinterpret_cast<struct mixer_name*>(0x1111)));
+
+ controls = NULL;
+ for (i = 1; i < ARRAY_SIZE(coupled_names); i++) {
+ controls = mixer_name_add(controls, coupled_names[i],
+ CRAS_STREAM_OUTPUT, MIXER_NAME_VOLUME);
+ }
+ /* Add controls to the list of coupled controls for this section. */
+ EXPECT_EQ(0, ucm_section_concat_coupled(section, controls));
+
+ i = 0;
+ DL_FOREACH(section->coupled, m_name) {
+ EXPECT_NE(m_name->name, coupled_names[i]);
+ EXPECT_EQ(0, strcmp(m_name->name, coupled_names[i]));
+ i++;
+ }
+ EXPECT_EQ(i, ARRAY_SIZE(coupled_names));
+
+ DL_APPEND(section_list, section);
+ ucm_section_free_list(section_list);
+}
+
+TEST(AlsaUcm, GetSections) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ struct ucm_section* sections;
+ struct ucm_section* section;
+ struct mixer_name* m_name;
+ int section_count = 0;
+ int i = 0;
+ const char *devices[] = { "Headphone", "The headphones jack.",
+ "Speaker", "The speakers.",
+ "Mic", "Microphone jack.",
+ "Internal Mic", "Internal Microphones",
+ "HDMI", "HDMI output" };
+ const char* ids[] = {
+ "=PlaybackPCM/Headphone/HiFi",
+ "=JackName/Headphone/HiFi",
+ "=JackType/Headphone/HiFi",
+ "=JackSwitch/Headphone/HiFi",
+ "=CoupledMixers/Headphone/HiFi",
+
+ "=PlaybackPCM/Speaker/HiFi",
+ "=CoupledMixers/Speaker/HiFi",
+
+ "=CapturePCM/Mic/HiFi",
+ "=JackName/Mic/HiFi",
+ "=JackType/Mic/HiFi",
+ "=JackSwitch/Mic/HiFi",
+ "=MixerName/Mic/HiFi",
+
+ "=CapturePCM/Internal Mic/HiFi",
+ "=CoupledMixers/Internal Mic/HiFi",
+ "=JackSwitch/Internal Mic/HiFi",
+
+ "=PlaybackPCM/HDMI/HiFi",
+ "=MixerName/HDMI/HiFi",
+
+ NULL
+ };
+ const char* values[] = {
+ "hw:my-sound-card,0",
+ "my-sound-card Headset Jack",
+ "gpio",
+ "2",
+ "HP-L,HP-R",
+
+ "hw:my-sound-card,0",
+ "SPK-L,SPK-R",
+
+ "hw:my-sound-card,0",
+ "my-sound-card Headset Jack",
+ "gpio",
+ "0",
+ "CAPTURE",
+
+ "hw:my-sound-card,0",
+ "MIC-L,MIC-R",
+ "-10",
+
+ "hw:my-sound-card,2",
+ "HDMI",
+ };
+
+ ResetStubData();
+
+ fake_list["_devices/HiFi"] = devices;
+ fake_list_size["_devices/HiFi"] = ARRAY_SIZE(devices);
+
+ while (ids[i]) {
+ snd_use_case_get_value[ids[i]] = values[i];
+ i++;
+ }
+
+ sections = ucm_get_sections(mgr);
+ ASSERT_NE(sections, (struct ucm_section*)NULL);
+ DL_FOREACH(sections, section) {
+ section_count++;
+ }
+ EXPECT_EQ(section_count, ARRAY_SIZE(devices) / 2);
+
+ // Headphone
+ section = sections;
+ EXPECT_EQ(0, strcmp(section->name, "Headphone"));
+ EXPECT_EQ(0, section->dev_idx);
+ EXPECT_EQ(CRAS_STREAM_OUTPUT, section->dir);
+ EXPECT_EQ(0, strcmp(section->jack_name, values[1]));
+ EXPECT_EQ(0, strcmp(section->jack_type, values[2]));
+ EXPECT_EQ(NULL, section->mixer_name);
+ ASSERT_NE((struct mixer_name*)NULL, section->coupled);
+ m_name = section->coupled;
+ EXPECT_EQ(0, strcmp(m_name->name, "HP-L"));
+ m_name = m_name->next;
+ EXPECT_EQ(0, strcmp(m_name->name, "HP-R"));
+ EXPECT_EQ(NULL, m_name->next);
+ EXPECT_EQ(2, section->jack_switch);
+
+ // Speaker
+ section = section->next;
+ EXPECT_EQ(0, strcmp(section->name, "Speaker"));
+ EXPECT_EQ(0, 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);
+ ASSERT_NE((struct mixer_name*)NULL, section->coupled);
+ m_name = section->coupled;
+ EXPECT_EQ(0, strcmp(m_name->name, "SPK-L"));
+ m_name = m_name->next;
+ EXPECT_EQ(0, strcmp(m_name->name, "SPK-R"));
+ EXPECT_EQ(NULL, m_name->next);
+
+ // Mic
+ section = section->next;
+ EXPECT_EQ(0, strcmp(section->name, "Mic"));
+ EXPECT_EQ(0, 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, values[2]));
+ EXPECT_EQ(0, section->jack_switch);
+ ASSERT_NE((const char *)NULL, section->mixer_name);
+ EXPECT_EQ(0, strcmp(section->mixer_name, "CAPTURE"));
+ EXPECT_EQ(NULL, section->coupled);
+
+ // Internal Mic
+ section = section->next;
+ EXPECT_EQ(0, strcmp(section->name, "Internal Mic"));
+ EXPECT_EQ(0, section->dev_idx);
+ EXPECT_EQ(CRAS_STREAM_INPUT, 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);
+ ASSERT_NE((struct mixer_name*)NULL, section->coupled);
+ m_name = section->coupled;
+ EXPECT_EQ(0, strcmp(m_name->name, "MIC-L"));
+ m_name = m_name->next;
+ EXPECT_EQ(0, strcmp(m_name->name, "MIC-R"));
+
+ // HDMI
+ section = section->next;
+ EXPECT_EQ(0, strcmp(section->name, "HDMI"));
+ EXPECT_EQ(2, 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);
+ ASSERT_NE((const char *)NULL, section->mixer_name);
+ EXPECT_EQ(0, strcmp(section->mixer_name, "HDMI"));
+
+ EXPECT_EQ(NULL, section->next);
+ ucm_section_free_list(sections);
+}
+
+TEST(AlsaUcm, GetSectionsMissingPCM) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ struct ucm_section* sections;
+ int i = 0;
+ const char *devices[] = { "Headphone", "The headphones jack." };
+ const char* ids[] = {
+ "=JackName/Headphone/HiFi",
+ "=CoupledMixers/Headphone/HiFi",
+ NULL
+ };
+ const char* values[] = {
+ "my-sound-card Headset Jack",
+ "HP-L,HP-R",
+ };
+
+ ResetStubData();
+
+ fake_list["_devices/HiFi"] = devices;
+ fake_list_size["_devices/HiFi"] = ARRAY_SIZE(devices);
+
+ while (ids[i]) {
+ snd_use_case_get_value[ids[i]] = values[i];
+ i++;
+ }
+
+ sections = ucm_get_sections(mgr);
+ EXPECT_EQ(NULL, sections);
+}
+
+TEST(AlsaUcm, GetSectionsBadPCM) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+ struct ucm_section* sections;
+ int i = 0;
+ const char *devices[] = { "Headphone", "The headphones jack." };
+ const char* ids[] = {
+ "=PlaybackPCM/Headphone/HiFi",
+ "=JackName/Headphone/HiFi",
+ "=CoupledMixers/Headphone/HiFi",
+ NULL
+ };
+ const char* values[] = {
+ "hw:my-sound-card:0",
+ "my-sound-card Headset Jack",
+ "HP-L,HP-R",
+ };
+
+ ResetStubData();
+
+ fake_list["_devices/HiFi"] = devices;
+ fake_list_size["_devices/HiFi"] = ARRAY_SIZE(devices);
+
+ while (ids[i]) {
+ snd_use_case_get_value[ids[i]] = values[i];
+ i++;
+ }
+
+ sections = ucm_get_sections(mgr);
+ EXPECT_EQ(NULL, sections);
+}
+
+TEST(AlsaUcm, CheckUseCaseVerbs) {
+ struct cras_use_case_mgr *mgr = &cras_ucm_mgr;
+
+ /* Verifies the mapping between stream types and verbs are correct. */
+ mgr->use_case = CRAS_STREAM_TYPE_DEFAULT;
+ EXPECT_EQ(0, strcmp("HiFi", uc_verb(mgr)));
+ mgr->use_case = CRAS_STREAM_TYPE_MULTIMEDIA;
+ EXPECT_EQ(0, strcmp("Multimedia", uc_verb(mgr)));
+ mgr->use_case = CRAS_STREAM_TYPE_VOICE_COMMUNICATION;
+ EXPECT_EQ(0, strcmp("Voice Call", uc_verb(mgr)));
+ mgr->use_case = CRAS_STREAM_TYPE_SPEECH_RECOGNITION;
+ EXPECT_EQ(0, strcmp("Speech", uc_verb(mgr)));
+ mgr->use_case = CRAS_STREAM_TYPE_PRO_AUDIO;
+ EXPECT_EQ(0, strcmp("Pro Audio", uc_verb(mgr)));
+}
+
+TEST(AlsaUcm, GetAvailUseCases) {
+ struct cras_use_case_mgr *mgr;
+ const char *verbs[] = { "HiFi", "Comment for Verb1",
+ "Voice Call", "Comment for Verb2",
+ "Speech", "Comment for Verb3" };
+
+ ResetStubData();
+
+ fake_list["_verbs"] = verbs;
+ fake_list_size["_verbs"] = 6;
+
+ mgr = ucm_create("foo");
+ EXPECT_EQ(0x0D, mgr->avail_use_cases);
+ ucm_destroy(mgr);
+}
+
+TEST(AlsaUcm, SetUseCase) {
+ struct cras_use_case_mgr *mgr;
+ const char *verbs[] = { "HiFi", "Comment for Verb1",
+ "Voice Call", "Comment for Verb2",
+ "Speech", "Comment for Verb3" };
+ int rc;
+
+ ResetStubData();
+
+ fake_list["_verbs"] = verbs;
+ fake_list_size["_verbs"] = 6;
+
+ mgr = ucm_create("foo");
+ EXPECT_EQ(snd_use_case_set_param[0],
+ std::make_pair(std::string("_verb"), std::string("HiFi")));
+
+ rc = ucm_set_use_case(mgr, CRAS_STREAM_TYPE_VOICE_COMMUNICATION);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(mgr->use_case, CRAS_STREAM_TYPE_VOICE_COMMUNICATION);
+ EXPECT_EQ(snd_use_case_set_param[1],
+ std::make_pair(std::string("_verb"), std::string("Voice Call")));
+
+ /* Request unavailable use case will fail. */
+ rc = ucm_set_use_case(mgr, CRAS_STREAM_TYPE_PRO_AUDIO);
+ EXPECT_EQ(-1, rc);
+ /* cras_use_case_mgr's use case should not be changed. */
+ EXPECT_EQ(mgr->use_case, CRAS_STREAM_TYPE_VOICE_COMMUNICATION);
+ /* And snd_use_case_set not being called. */
+ EXPECT_EQ(2, snd_use_case_set_param.size());
+
+ ucm_destroy(mgr);
+}
+
/* Stubs */
extern "C" {
@@ -485,9 +1410,13 @@ int snd_use_case_get(snd_use_case_mgr_t* uc_mgr,
const char *identifier,
const char **value) {
snd_use_case_get_called++;
- *value = strdup(snd_use_case_get_value[identifier].c_str());
snd_use_case_get_id.push_back(std::string(identifier));
- return snd_use_case_get_ret_value[identifier];
+ if (snd_use_case_get_value.find(identifier) == snd_use_case_get_value.end()) {
+ *value = NULL;
+ return -1;
+ }
+ *value = strdup(snd_use_case_get_value[identifier].c_str());
+ return 0;
}
int snd_use_case_set(snd_use_case_mgr_t* uc_mgr,
@@ -496,7 +1425,7 @@ int snd_use_case_set(snd_use_case_mgr_t* uc_mgr,
snd_use_case_set_called++;
snd_use_case_set_param.push_back(
std::make_pair(std::string(identifier), std::string(value)));
- return snd_use_case_set_return;;
+ return snd_use_case_set_return;
}
int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr,
@@ -517,5 +1446,6 @@ int snd_use_case_free_list(const char *list[], int items) {
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ openlog(NULL, LOG_PERROR, LOG_USER);
return RUN_ALL_TESTS();
}
diff --git a/cras/src/tests/audio_area_unittest.cc b/cras/src/tests/audio_area_unittest.cc
index 9acf22eb..7efe3a62 100644
--- a/cras/src/tests/audio_area_unittest.cc
+++ b/cras/src/tests/audio_area_unittest.cc
@@ -48,7 +48,7 @@ TEST(AudioArea, CopyAudioArea) {
memset(buf1, 0, 32 * 2);
for (i = 0; i < 32; i++)
buf2[i] = rand();
- cras_audio_area_copy(a1, 0, &fmt, a2, 0, 0);
+ cras_audio_area_copy(a1, 0, &fmt, a2, 0, 1.0);
for (i = 0; i < 32; i++)
EXPECT_EQ(buf1[i], buf2[i]);
@@ -56,6 +56,44 @@ TEST(AudioArea, CopyAudioArea) {
cras_audio_area_destroy(a2);
}
+TEST(AudioArea, CopyAudioAreaWithGain) {
+ struct cras_audio_format fmt;
+ int i;
+ /* Check a gain of 10x can be applied. */
+ float gain_scaler = 10.0f;
+
+ fmt.num_channels = 2;
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ for (i = 0; i < CRAS_CH_MAX; i++)
+ fmt.channel_layout[i] = stereo[i];
+
+ a1 = cras_audio_area_create(2);
+ a2 = cras_audio_area_create(2);
+ cras_audio_area_config_channels(a1, &fmt);
+ cras_audio_area_config_channels(a2, &fmt);
+ cras_audio_area_config_buf_pointers(a1, &fmt, (uint8_t *)buf1);
+ cras_audio_area_config_buf_pointers(a2, &fmt, (uint8_t *)buf2);
+ a1->frames = 16;
+ a2->frames = 16;
+
+ memset(buf1, 0, 32 * 2);
+ /* Let src has some samples smaller than 32768/10 and some samples larger than
+ * 32768/10 to test clipping. */
+ for (i = 0; i < 16; i++)
+ buf2[i] = rand() % 3270;
+ for (i = 17; i < 32; i++)
+ buf2[i] = 3280 + rand() % 3200;
+ cras_audio_area_copy(a1, 0, &fmt, a2, 0, gain_scaler);
+ for (i = 0; i < 32; i++) {
+ int32_t expected_value = buf2[i] * gain_scaler;
+ if (expected_value > INT16_MAX)
+ expected_value = INT16_MAX;
+ EXPECT_EQ(buf1[i], expected_value);
+ }
+
+ cras_audio_area_destroy(a1);
+ cras_audio_area_destroy(a2);
+}
TEST(AudioArea, CopyAudioAreaOffset) {
struct cras_audio_format fmt;
int i;
@@ -74,14 +112,14 @@ TEST(AudioArea, CopyAudioAreaOffset) {
a1->frames = 16;
a2->frames = 14;
- memset(buf1, 0x55, 32 * 2);
+ memset(buf1, 0, 32 * 2);
for (i = 0; i < 32; i++)
buf2[i] = rand();
- cras_audio_area_copy(a1, 2, &fmt, a2, 0, 0);
- EXPECT_EQ(buf1[0], 0x5555);
- EXPECT_EQ(buf1[1], 0x5555);
- EXPECT_EQ(buf1[2], 0x5555);
- EXPECT_EQ(buf1[3], 0x5555);
+ cras_audio_area_copy(a1, 2, &fmt, a2, 0, 1.0);
+ EXPECT_EQ(buf1[0], 0);
+ EXPECT_EQ(buf1[1], 0);
+ EXPECT_EQ(buf1[2], 0);
+ EXPECT_EQ(buf1[3], 0);
for (i = 4; i < 32; i++)
EXPECT_EQ(buf1[i], buf2[i-4]);
@@ -107,20 +145,20 @@ TEST(AudioArea, CopyAudioAreaOffsetLimit) {
a1->frames = 14;
a2->frames = 14;
- memset(buf1, 0x55, 32 * 2);
+ memset(buf1, 0, 32 * 2);
for (i = 0; i < 32; i++)
buf2[i] = rand();
- cras_audio_area_copy(a1, 2, &fmt, a2, 0, 0);
- EXPECT_EQ(buf1[0], 0x5555);
- EXPECT_EQ(buf1[1], 0x5555);
- EXPECT_EQ(buf1[2], 0x5555);
- EXPECT_EQ(buf1[3], 0x5555);
+ cras_audio_area_copy(a1, 2, &fmt, a2, 0, 1.0);
+ EXPECT_EQ(buf1[0], 0);
+ EXPECT_EQ(buf1[1], 0);
+ EXPECT_EQ(buf1[2], 0);
+ EXPECT_EQ(buf1[3], 0);
for (i = 4; i < 28; i++)
EXPECT_EQ(buf1[i], buf2[i-4]);
- EXPECT_EQ(buf1[28], 0x5555);
- EXPECT_EQ(buf1[29], 0x5555);
- EXPECT_EQ(buf1[30], 0x5555);
- EXPECT_EQ(buf1[31], 0x5555);
+ EXPECT_EQ(buf1[28], 0);
+ EXPECT_EQ(buf1[29], 0);
+ EXPECT_EQ(buf1[30], 0);
+ EXPECT_EQ(buf1[31], 0);
cras_audio_area_destroy(a1);
cras_audio_area_destroy(a2);
@@ -152,7 +190,7 @@ TEST(AudioArea, CopyMonoToStereo) {
memset(buf1, 0, 32 * 2);
for (i = 0; i < 32; i++)
buf2[i] = rand();
- cras_audio_area_copy(a1, 0, &dst_fmt, a2, 0, 0);
+ cras_audio_area_copy(a1, 0, &dst_fmt, a2, 0, 1.0);
for (i = 0; i < 16; i++) {
EXPECT_EQ(buf1[i * 2], buf2[i]);
EXPECT_EQ(buf1[i * 2 + 1], buf2[i]);
@@ -186,7 +224,7 @@ TEST(AudioArea, CopyStereoToMono) {
memset(buf1, 0, 32 * 2);
for (i = 0; i < 32; i++)
buf2[i] = rand() % 10000;
- cras_audio_area_copy(a1, 0, &fmt, a2, 0, 0);
+ cras_audio_area_copy(a1, 0, &fmt, a2, 0, 1.0);
for (i = 0; i < 16; i++)
EXPECT_EQ(buf1[i], buf2[i * 2] + buf2[i * 2 + 1]);
@@ -218,7 +256,7 @@ TEST(AudioArea, KeyboardMicCopyStereo) {
memset(buf1, 0, 32 * 2);
for (i = 0; i < 32; i++)
buf2[i] = rand();
- cras_audio_area_copy(a1, 0, &fmt, a2, 0, 0);
+ cras_audio_area_copy(a1, 0, &fmt, a2, 0, 1.0);
for (i = 0; i < 10; i++) {
EXPECT_EQ(buf1[i * 3], buf2[i * 2]);
EXPECT_EQ(buf1[i * 3 + 1], buf2[i * 2 + 1]);
@@ -257,7 +295,7 @@ TEST(AudioArea, KeyboardMicCopyFrontCenter) {
memset(buf1, 0, 32 * 2);
for (i = 0; i < 32; i++)
buf2[i] = rand();
- cras_audio_area_copy(a1, 0, &dst_fmt, a2, 0, 0);
+ cras_audio_area_copy(a1, 0, &dst_fmt, a2, 0, 1.0);
for (i = 0; i < 10; i++) {
EXPECT_EQ(buf1[i * 3], 0);
EXPECT_EQ(buf1[i * 3 + 1], 0);
@@ -272,15 +310,15 @@ TEST(AudioArea, KeyboardMicCopyFrontCenter) {
extern "C" {
-void cras_mix_add_stride(snd_pcm_format_t fmt, uint8_t *dst, uint8_t *src,
+void cras_mix_add_scale_stride(snd_pcm_format_t fmt, uint8_t *dst, uint8_t *src,
unsigned int count, unsigned int dst_stride,
- unsigned int src_stride)
+ unsigned int src_stride, float scaler)
{
unsigned int i;
for (i = 0; i < count; i++) {
int32_t sum;
- sum = *(int16_t *)dst + *(int16_t *)src;
+ sum = *(int16_t *)dst + *(int16_t *)src * scaler;
if (sum > INT16_MAX)
sum = INT16_MAX;
else if (sum < INT16_MIN)
diff --git a/cras/src/tests/audio_test_gui.py b/cras/src/tests/audio_test_gui.py
new file mode 100644
index 00000000..6ee401be
--- /dev/null
+++ b/cras/src/tests/audio_test_gui.py
@@ -0,0 +1,266 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 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.
+"""Script functions as a web app and wrapper for the cras_router program."""
+
+import re
+import subprocess
+import logging
+import cherrypy
+
+# Node Format: [Stable_Id, ID, Vol, Plugged, L/R_swapped, Time, Type, Name]
+ID_INDEX = 1
+PLUGGED_INDEX = 3
+TYPE_INDEX = 6
+NAME_INDEX = 7
+
+
+def get_plugged_nodes(plugged_nodes, lines, is_input):
+ start_str = 'Input Nodes:' if is_input else 'Output Nodes:'
+ end_str = 'Attached clients:' if is_input else 'Input Devices:'
+ for i in range(lines.index(start_str) + 2,
+ lines.index(end_str)):
+ node = filter(None, re.split(r'\s+|\*+', lines[i]))
+ # check for nodes that are plugged nodes and loopback
+ if node[PLUGGED_INDEX] == 'yes' and node[TYPE_INDEX][:4] != 'POST':
+ key = node[TYPE_INDEX] + ' ' + node[NAME_INDEX]
+ plugged_nodes[key] = node[ID_INDEX]
+
+
+class CrasRouterTest(object):
+ """Cherrypy class that builds and runs the HTML for audio testing tool."""
+
+ @cherrypy.expose
+ def index(self):
+ """Builds up and displays the html for the audio testing tool.
+
+ Returns:
+ html that was built up based on plugged audio devices.
+ """
+
+ # Stop program if currently being run.
+ if 'process' in cherrypy.session:
+ print 'Existing process'
+ # If return code is None process is still running
+ if cherrypy.session['process'].poll() is None:
+ print 'Killing existing process'
+ cherrypy.session['process'].kill()
+ else:
+ print 'Process already finished'
+
+ html = """<html>
+ <head>
+ <title>Audio Test</title>
+ </head>
+ <body style="background-color:lightgrey;">
+ <font color="red">
+ <h1>Audio Closed Loop Test</h1>
+ <font style="color:rgb(100, 149, 237)">
+ <h3>
+ <form name="routerOptions" method="get"
+ onsubmit="return validateForm()" action="start_test">
+ <h2>Input Type</h2>
+ """
+ dump = subprocess.check_output(['cras_test_client', '--dump_s'])
+ if not dump:
+ return 'Could not connect to server'
+ dump_lines = dump.split('\n')
+ input_plugged_nodes = {}
+ get_plugged_nodes(input_plugged_nodes, dump_lines, True)
+ for name, node_id in input_plugged_nodes.items():
+ line = '<input type ="radio" name="input_type" value="'
+ line += node_id + '">' +name + '<br>\n'
+ html += line
+
+ html += """<input type ="radio" id="i0" name="input_type"
+ value="file">File<br>
+ <div id="input_file" style="display:none;">
+ Filename <input type="text" name="input_file"><br><br>
+ </div>
+ <h2>Output Type</h2>"""
+ output_plugged_nodes = {}
+ get_plugged_nodes(output_plugged_nodes, dump_lines, False)
+ for name, node_id in output_plugged_nodes.items():
+ line = '<input type ="radio" name="output_type" value="'
+ line = line + node_id + '">' +name + '<br>\n'
+ html += line
+
+ html += """<input type ="radio" name="output_type"
+ value="file">File<br>
+ <div id="output_file" style="display:none;">
+ Filename <input type="text" name="output_file">
+ </div><br>
+ <h2>Sample Rate</h2>
+ <input type="radio" name="rate" id="sample_rate1" value=48000
+ checked>48,000 Hz<br>
+ <input type="radio" name="rate" id="sample_rate0" value=44100>
+ 44,100 Hz<br><br>
+ <button type="submit" onclick="onOff(this)">Test!</button>
+ </h3>
+ </form>
+ </font>
+ </body>
+ </html>
+ """
+ javascript = """
+ <script>
+ /* Does basic error checking to make sure user doesn't
+ * give bad options to the router.
+ */
+ function validateForm(){
+ var input_type =
+ document.forms['routerOptions']['input_type'].value;
+ var output_type =
+ document.forms['routerOptions']['output_type'].value;
+ if (input_type == '' || output_type == '') {
+ alert('Please select an input and output type.');
+ return false;
+ }
+ if (input_type == 'file' && output_type == 'file') {
+ alert('Input and Output Types cannot both be files!');
+ return false;
+ }
+ //check if filename is valid
+ if (input_type == 'file') {
+ var input_file =
+ document.forms['routerOptions']['input_file'].value;
+ if (input_file == '') {
+ alert('Please enter a file name');
+ return false;
+ }
+ }
+ if (output_type == 'file') {
+ var output_file =
+ document.forms['routerOptions']['output_file'].value;
+ if (output_file == '') {
+ alert('Please enter a file name');
+ return false;
+ }
+ }
+ }
+
+ function show_filename(radio, file_elem) {
+ for(var i =0; i < radio.length; i++){
+ radio[i].onclick = function(){
+ if (this.value == 'file') {
+ file_elem.style.display = 'block';
+ } else {
+ file_elem.style.display = 'none';
+ }
+ }
+ }
+ }
+ /* Loops determine if filename field should be shown */
+ var input_type_rad =
+ document.forms['routerOptions']['input_type'];
+ var input_file_elem =
+ document.getElementById('input_file');
+ var output_type_rad =
+ document.forms['routerOptions']['output_type'];
+ var output_file_elem =
+ document.getElementById('output_file');
+ show_filename(input_type_rad, input_file_elem);
+ show_filename(output_type_rad, output_file_elem);
+ </script>"""
+ html += javascript
+ return html
+
+ @cherrypy.expose
+ def start_test(self, input_type, output_type, input_file='',
+ output_file='', rate=48000):
+ """Capture audio from the input_type and plays it back to the output_type.
+
+ Args:
+ input_type: Node id for the selected input or 'file' for files
+ output_type: Node id for the selected output or 'file' for files
+ input_file: Path of the input if 'file' is input type
+ output_file: Path of the output if 'file' is output type
+ rate: Sample rate for the test.
+
+ Returns:
+ html for the tesing in progress page.
+ """
+ print 'Beginning test'
+ if input_type == 'file' or output_type == 'file':
+ command = ['cras_test_client']
+ else:
+ command = ['cras_router']
+ if input_type == 'file':
+ command.append('--playback_file')
+ command.append(str(input_file))
+ else:
+ set_input = ['cras_test_client', '--select_input', str(input_type)]
+ if subprocess.check_call(set_input):
+ print 'Error setting input'
+ if output_type == 'file':
+ command.append('--capture_file')
+ command.append(str(output_file))
+ else:
+ set_output = ['cras_test_client', '--select_output', str(output_type)]
+ if subprocess.check_call(set_output):
+ print 'Error setting output'
+ command.append('--rate')
+ command.append(str(rate))
+ print 'Running commmand: ' + str(command)
+ p = subprocess.Popen(command)
+ cherrypy.session['process'] = p
+ return """
+ <html>
+ <head>
+ <style type="text/css">
+ body {
+ background-color: #DC143C;
+ }
+ #test {
+ color: white;
+ text-align: center;
+ }
+ </style>
+ <title>Running test</title>
+ </head>
+ <body>
+ <div id="test">
+ <h1>Test in progress</h1>
+ <form action="index"><!--Go back to orginal page-->
+ <button type="submit" id="stop">Click to stop</button>
+ </form>
+ <h2>Time Elapsed<br>
+ <time id="elapsed_time">00:00</time>
+ </h2>
+ </div>
+ </body>
+ </html>
+ <script type="text/javascript">
+ var seconds = 0;
+ var minutes = 0;
+ var elapsed_time;
+ var start_time = new Date().getTime();
+ function secondPassed(){
+ var time = new Date().getTime() - start_time;
+ elapsed_time = Math.floor(time / 100) / 10;
+ minutes = Math.floor(elapsed_time / 60);
+ seconds = Math.floor(elapsed_time % 60);
+ var seconds_str = (seconds < 10 ? '0' + seconds: '' + seconds);
+ var minutes_str = (minutes < 10 ? '0' + minutes: '' + minutes);
+ var time_passed = minutes_str + ':' + seconds_str;
+ document.getElementById("elapsed_time").textContent = time_passed;
+ }
+ //have time tic every second
+ var timer = setInterval(secondPassed, 1000);
+ var stop = document.getElementById("stop");
+ stop.onclick = function(){
+ seconds = 0;
+ minutes = 0;
+ clearInterval(timer);
+ }
+ </script>"""
+
+if __name__ == '__main__':
+ conf = {
+ '/': {
+ 'tools.sessions.on': True
+ }
+ }
+ cherrypy.quickstart(CrasRouterTest(), '/', conf)
diff --git a/cras/src/tests/audio_thread_unittest.cc b/cras/src/tests/audio_thread_unittest.cc
index da4db8b6..e1501260 100644
--- a/cras/src/tests/audio_thread_unittest.cc
+++ b/cras/src/tests/audio_thread_unittest.cc
@@ -4,11 +4,15 @@
extern "C" {
#include "audio_thread.c"
+#include "cras_audio_area.h"
}
#include <gtest/gtest.h>
+#include <map>
#define MAX_CALLS 10
+#define BUFFER_SIZE 8192
+#define FIRST_CB_LEVEL 480
static unsigned int cras_rstream_dev_offset_called;
static unsigned int cras_rstream_dev_offset_ret[MAX_CALLS];
@@ -18,13 +22,65 @@ static unsigned int cras_rstream_dev_offset_update_called;
static const struct cras_rstream *cras_rstream_dev_offset_update_rstream_val[MAX_CALLS];
static unsigned int cras_rstream_dev_offset_update_frames_val[MAX_CALLS];
static unsigned int cras_rstream_dev_offset_update_dev_id_val[MAX_CALLS];
+static int cras_iodev_all_streams_written_ret;
+static struct cras_audio_area *cras_iodev_get_output_buffer_area;
+static int cras_iodev_put_output_buffer_called;
+static unsigned int cras_iodev_put_output_buffer_nframes;
+static unsigned int cras_iodev_fill_odev_zeros_frames;
+static int dev_stream_playback_frames_ret;
+static unsigned int cras_iodev_prepare_output_before_write_samples_called;
+static enum CRAS_IODEV_STATE cras_iodev_prepare_output_before_write_samples_state;
+static unsigned int cras_iodev_get_output_buffer_called;
+static int cras_iodev_prepare_output_before_write_samples_ret;
+static int cras_iodev_reset_request_called;
+static struct cras_iodev *cras_iodev_reset_request_iodev;
+static int cras_iodev_output_underrun_called;
+static int cras_device_monitor_reset_device_called;
+static struct cras_iodev *cras_device_monitor_reset_device_iodev;
+static struct cras_iodev *cras_iodev_start_ramp_odev;
+static enum CRAS_IODEV_RAMP_REQUEST cras_iodev_start_ramp_request;
+static std::map<const struct dev_stream*, struct timespec> dev_stream_wake_time_val;
+
+void ResetGlobalStubData() {
+ cras_rstream_dev_offset_called = 0;
+ cras_rstream_dev_offset_update_called = 0;
+ for (int i = 0; i < MAX_CALLS; i++) {
+ cras_rstream_dev_offset_ret[i] = 0;
+ cras_rstream_dev_offset_rstream_val[i] = NULL;
+ cras_rstream_dev_offset_dev_id_val[i] = 0;
+ cras_rstream_dev_offset_update_rstream_val[i] = NULL;
+ cras_rstream_dev_offset_update_frames_val[i] = 0;
+ cras_rstream_dev_offset_update_dev_id_val[i] = 0;
+ }
+ cras_iodev_all_streams_written_ret = 0;
+ if (cras_iodev_get_output_buffer_area) {
+ free(cras_iodev_get_output_buffer_area);
+ cras_iodev_get_output_buffer_area = NULL;
+ }
+ cras_iodev_put_output_buffer_called = 0;
+ cras_iodev_put_output_buffer_nframes = 0;
+ cras_iodev_fill_odev_zeros_frames = 0;
+ dev_stream_playback_frames_ret = 0;
+ cras_iodev_prepare_output_before_write_samples_called = 0;
+ cras_iodev_prepare_output_before_write_samples_state = CRAS_IODEV_STATE_OPEN;
+ cras_iodev_get_output_buffer_called = 0;
+ cras_iodev_prepare_output_before_write_samples_ret = 0;
+ cras_iodev_reset_request_called = 0;
+ cras_iodev_reset_request_iodev = NULL;
+ cras_iodev_output_underrun_called = 0;
+ cras_device_monitor_reset_device_called = 0;
+ cras_device_monitor_reset_device_iodev = NULL;
+ cras_iodev_start_ramp_odev = NULL;
+ cras_iodev_start_ramp_request = CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK;
+ dev_stream_wake_time_val.clear();
+}
// Test streams and devices manipulation.
class StreamDeviceSuite : public testing::Test {
protected:
virtual void SetUp() {
- device_id_ = 0;
thread_ = audio_thread_create();
+ ResetStubData();
}
virtual void TearDown() {
@@ -37,14 +93,23 @@ class StreamDeviceSuite : public testing::Test {
iodev->direction = direction;
iodev->open_dev = open_dev;
iodev->close_dev = close_dev;
- iodev->dev_running = dev_running;
- iodev->is_open = is_open;
iodev->frames_queued = frames_queued;
iodev->delay_frames = delay_frames;
iodev->get_buffer = get_buffer;
iodev->put_buffer = put_buffer;
iodev->flush_buffer = flush_buffer;
iodev->ext_format = &format_;
+ iodev->buffer_size = BUFFER_SIZE;
+ iodev->min_cb_level = FIRST_CB_LEVEL;
+ }
+
+ void ResetStubData() {
+ device_id_ = 0;
+ open_dev_called_ = 0;
+ close_dev_called_ = 0;
+ frames_queued_ = 0;
+ delay_frames_ = 0;
+ audio_buffer_size_ = 0;
}
void SetupRstream(struct cras_rstream *rstream,
@@ -52,6 +117,12 @@ class StreamDeviceSuite : public testing::Test {
memset(rstream, 0, sizeof(*rstream));
rstream->direction = direction;
rstream->cb_threshold = 480;
+ rstream->shm.area = static_cast<cras_audio_shm_area*>(
+ calloc(1, sizeof(rstream->shm.area)));
+ }
+
+ void TearDownRstream(struct cras_rstream *rstream) {
+ free(rstream->shm.area);
}
void SetupPinnedStream(struct cras_rstream *rstream,
@@ -72,16 +143,8 @@ class StreamDeviceSuite : public testing::Test {
return 0;
}
- static int dev_running(const cras_iodev* iodev) {
- dev_running_called_++;
- return 1;
- }
-
- static int is_open(const cras_iodev* iodev) {
- return is_open_;
- }
-
- static int frames_queued(const cras_iodev* iodev) {
+ static int frames_queued(const cras_iodev* iodev, struct timespec* tstamp) {
+ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
return frames_queued_;
}
@@ -125,26 +188,22 @@ class StreamDeviceSuite : public testing::Test {
static int open_dev_called_;
static int close_dev_called_;
- static int dev_running_called_;
- static int is_open_;
static int frames_queued_;
static int delay_frames_;
static struct cras_audio_format format_;
static struct cras_audio_area *area_;
- static uint8_t audio_buffer_[8192];
+ static uint8_t audio_buffer_[BUFFER_SIZE];
static unsigned int audio_buffer_size_;
};
-int StreamDeviceSuite::open_dev_called_ = 0;
-int StreamDeviceSuite::close_dev_called_ = 0;
-int StreamDeviceSuite::dev_running_called_ = 0;
-int StreamDeviceSuite::is_open_ = 0;
-int StreamDeviceSuite::frames_queued_ = 0;
-int StreamDeviceSuite::delay_frames_ = 0;
+int StreamDeviceSuite::open_dev_called_;
+int StreamDeviceSuite::close_dev_called_;
+int StreamDeviceSuite::frames_queued_;
+int StreamDeviceSuite::delay_frames_;
struct cras_audio_format StreamDeviceSuite::format_;
struct cras_audio_area *StreamDeviceSuite::area_;
uint8_t StreamDeviceSuite::audio_buffer_[8192];
-unsigned int StreamDeviceSuite::audio_buffer_size_ = 0;
+unsigned int StreamDeviceSuite::audio_buffer_size_;
TEST_F(StreamDeviceSuite, AddRemoveOpenOutputDevice) {
struct cras_iodev iodev;
@@ -162,6 +221,38 @@ TEST_F(StreamDeviceSuite, AddRemoveOpenOutputDevice) {
EXPECT_EQ(NULL, adev);
}
+TEST_F(StreamDeviceSuite, StartRamp) {
+ struct cras_iodev iodev;
+ struct open_dev *adev;
+ int rc;
+ enum CRAS_IODEV_RAMP_REQUEST req;
+
+ SetupDevice(&iodev, CRAS_STREAM_OUTPUT);
+
+ // Check the newly added device is open.
+ thread_add_open_dev(thread_, &iodev);
+ adev = thread_->open_devs[CRAS_STREAM_OUTPUT];
+ EXPECT_EQ(adev->dev, &iodev);
+
+ // Ramp up for unmute.
+ req = CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE;
+ rc = thread_dev_start_ramp(thread_, &iodev, req);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(&iodev, cras_iodev_start_ramp_odev);
+ EXPECT_EQ(req, cras_iodev_start_ramp_request);
+
+ // Ramp down for mute.
+ ResetStubData();
+ req = CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE;
+
+ rc = thread_dev_start_ramp(thread_, &iodev, req);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(&iodev, cras_iodev_start_ramp_odev);
+ EXPECT_EQ(req, cras_iodev_start_ramp_request);
+}
+
TEST_F(StreamDeviceSuite, AddRemoveOpenInputDevice) {
struct cras_iodev iodev;
struct open_dev *adev;
@@ -271,6 +362,51 @@ TEST_F(StreamDeviceSuite, MultipleInputStreamsCopyFirstStreamOffset) {
EXPECT_EQ(30, cras_rstream_dev_offset_update_frames_val[0]);
EXPECT_EQ(&rstream2, cras_rstream_dev_offset_update_rstream_val[1]);
EXPECT_EQ(0, cras_rstream_dev_offset_update_frames_val[1]);
+
+ TearDownRstream(&rstream);
+ TearDownRstream(&rstream2);
+ TearDownRstream(&rstream3);
+}
+
+TEST_F(StreamDeviceSuite, InputStreamsSetInputDeviceWakeTime) {
+ struct cras_iodev iodev;
+ struct cras_iodev *iodevs[] = {&iodev};
+ struct cras_rstream rstream1, rstream2;
+ struct timespec ts_wake_1 = {.tv_sec = 1, .tv_nsec = 500};
+ struct timespec ts_wake_2 = {.tv_sec = 1, .tv_nsec = 1000};
+ struct open_dev *adev;
+
+ SetupDevice(&iodev, CRAS_STREAM_INPUT);
+ SetupRstream(&rstream1, CRAS_STREAM_INPUT);
+ SetupRstream(&rstream2, CRAS_STREAM_INPUT);
+
+ thread_add_open_dev(thread_, &iodev);
+ thread_add_stream(thread_, &rstream1, iodevs, 1);
+ thread_add_stream(thread_, &rstream2, iodevs, 1);
+ EXPECT_NE((void *)NULL, iodev.streams);
+
+ // Assume device is running.
+ iodev.state = CRAS_IODEV_STATE_NORMAL_RUN;
+
+ // Set stub data for dev_stream_wake_time.
+ dev_stream_wake_time_val[iodev.streams] = ts_wake_1;
+ dev_stream_wake_time_val[iodev.streams->next] = ts_wake_2;
+
+ // Send captured samples to client.
+ // This will also update wake time for this device based on
+ // dev_stream_wake_time of each stream of this device.
+ send_captured_samples(thread_);
+
+ // wake_ts is maintained in open_dev.
+ adev = thread_->open_devs[CRAS_STREAM_INPUT];
+
+ // The wake up time for this device is the minimum of
+ // ts_wake_1 and ts_wake_2.
+ EXPECT_EQ(ts_wake_1.tv_sec, adev->wake_ts.tv_sec);
+ EXPECT_EQ(ts_wake_1.tv_nsec, adev->wake_ts.tv_nsec);
+
+ TearDownRstream(&rstream1);
+ TearDownRstream(&rstream2);
}
TEST_F(StreamDeviceSuite, AddRemoveMultipleStreamsOnMultipleDevices) {
@@ -334,6 +470,180 @@ TEST_F(StreamDeviceSuite, AddRemoveMultipleStreamsOnMultipleDevices) {
thread_add_open_dev(thread_, &iodev);
dev_stream = iodev.streams;
EXPECT_EQ(NULL, dev_stream);
+
+ TearDownRstream(&rstream);
+ TearDownRstream(&rstream2);
+ TearDownRstream(&rstream3);
+}
+
+TEST_F(StreamDeviceSuite, WriteOutputSamplesPrepareOutputFailed) {
+ struct cras_iodev iodev;
+ struct open_dev *adev;
+
+ ResetGlobalStubData();
+
+ SetupDevice(&iodev, CRAS_STREAM_OUTPUT);
+
+ // Add the device.
+ thread_add_open_dev(thread_, &iodev);
+ adev = thread_->open_devs[CRAS_STREAM_OUTPUT];
+
+ // Assume device is started.
+ iodev.state = CRAS_IODEV_STATE_NO_STREAM_RUN;
+ // Assume device remains in no stream state;
+ cras_iodev_prepare_output_before_write_samples_state = \
+ CRAS_IODEV_STATE_NO_STREAM_RUN;
+
+ // Assume there is an error in prepare_output.
+ cras_iodev_prepare_output_before_write_samples_ret = -EINVAL;
+
+ // cras_iodev should handle no stream playback.
+ EXPECT_EQ(-EINVAL, write_output_samples(thread_, adev));
+
+ // cras_iodev_get_output_buffer in audio_thread write_output_samples is not
+ // called.
+ EXPECT_EQ(0, cras_iodev_get_output_buffer_called);
+
+ thread_rm_open_dev(thread_, &iodev);
+}
+
+TEST_F(StreamDeviceSuite, WriteOutputSamplesNoStream) {
+ struct cras_iodev iodev;
+ struct open_dev *adev;
+
+ ResetGlobalStubData();
+
+ SetupDevice(&iodev, CRAS_STREAM_OUTPUT);
+
+ // Add the device.
+ thread_add_open_dev(thread_, &iodev);
+ adev = thread_->open_devs[CRAS_STREAM_OUTPUT];
+
+ // Assume device is started.
+ iodev.state = CRAS_IODEV_STATE_NO_STREAM_RUN;
+ // Assume device remains in no stream state;
+ cras_iodev_prepare_output_before_write_samples_state = \
+ CRAS_IODEV_STATE_NO_STREAM_RUN;
+
+ // cras_iodev should handle no stream playback.
+ write_output_samples(thread_, adev);
+ EXPECT_EQ(1, cras_iodev_prepare_output_before_write_samples_called);
+ // cras_iodev_get_output_buffer in audio_thread write_output_samples is not
+ // called.
+ EXPECT_EQ(0, cras_iodev_get_output_buffer_called);
+
+ thread_rm_open_dev(thread_, &iodev);
+}
+
+TEST_F(StreamDeviceSuite, WriteOutputSamplesLeaveNoStream) {
+ struct cras_iodev iodev;
+ struct open_dev *adev;
+
+ ResetGlobalStubData();
+
+ SetupDevice(&iodev, CRAS_STREAM_OUTPUT);
+
+ // Setup the output buffer for device.
+ cras_iodev_get_output_buffer_area = cras_audio_area_create(2);
+
+ // Add the device.
+ thread_add_open_dev(thread_, &iodev);
+ adev = thread_->open_devs[CRAS_STREAM_OUTPUT];
+
+ // Assume device in no stream state.
+ iodev.state = CRAS_IODEV_STATE_NO_STREAM_RUN;
+
+ // Assume device remains in no stream state;
+ cras_iodev_prepare_output_before_write_samples_state = \
+ CRAS_IODEV_STATE_NO_STREAM_RUN;
+
+ // cras_iodev should NOT leave no stream state;
+ write_output_samples(thread_, adev);
+ EXPECT_EQ(1, cras_iodev_prepare_output_before_write_samples_called);
+ // cras_iodev_get_output_buffer in audio_thread write_output_samples is not
+ // called.
+ EXPECT_EQ(0, cras_iodev_get_output_buffer_called);
+
+ // Assume device leaves no stream state;
+ cras_iodev_prepare_output_before_write_samples_state = \
+ CRAS_IODEV_STATE_NORMAL_RUN;
+
+ // cras_iodev should write samples from streams.
+ write_output_samples(thread_, adev);
+ EXPECT_EQ(2, cras_iodev_prepare_output_before_write_samples_called);
+ EXPECT_EQ(1, cras_iodev_get_output_buffer_called);
+
+ thread_rm_open_dev(thread_, &iodev);
+}
+
+TEST_F(StreamDeviceSuite, WriteOutputSamplesUnderrun) {
+ struct cras_iodev iodev, *piodev = &iodev;
+ struct open_dev *adev;
+ struct cras_rstream rstream;
+
+ ResetGlobalStubData();
+
+ SetupDevice(&iodev, CRAS_STREAM_OUTPUT);
+ SetupRstream(&rstream, CRAS_STREAM_OUTPUT);
+
+ // Setup the output buffer for device.
+ cras_iodev_get_output_buffer_area = cras_audio_area_create(2);
+
+ // Add the device and add the stream.
+ thread_add_open_dev(thread_, &iodev);
+ adev = thread_->open_devs[CRAS_STREAM_OUTPUT];
+ thread_add_stream(thread_, &rstream, &piodev, 1);
+
+ // Assume device is running and there is an underrun. There is no frame
+ // queued and there is no sample written in this cycle.
+ // Audio thread should ask iodev to handle output underrun.
+ iodev.state = CRAS_IODEV_STATE_NORMAL_RUN;
+ frames_queued_ = 0;
+ cras_iodev_all_streams_written_ret = 0;
+
+ // Assume device in normal run stream state;
+ cras_iodev_prepare_output_before_write_samples_state = \
+ CRAS_IODEV_STATE_NORMAL_RUN;
+
+ write_output_samples(thread_, adev);
+ EXPECT_EQ(1, cras_iodev_output_underrun_called);
+
+ thread_rm_open_dev(thread_, &iodev);
+ TearDownRstream(&rstream);
+}
+
+TEST_F(StreamDeviceSuite, DoPlaybackUnderrun) {
+ struct cras_iodev iodev, *piodev = &iodev;
+ struct cras_rstream rstream;
+
+ ResetGlobalStubData();
+
+ SetupDevice(&iodev, CRAS_STREAM_OUTPUT);
+ SetupRstream(&rstream, CRAS_STREAM_OUTPUT);
+
+ // Setup the output buffer for device.
+ cras_iodev_get_output_buffer_area = cras_audio_area_create(2);
+
+ // Add the device and add the stream.
+ thread_add_open_dev(thread_, &iodev);
+ thread_add_stream(thread_, &rstream, &piodev, 1);
+
+ // Assume device is running and there is a severe underrun.
+ iodev.state = CRAS_IODEV_STATE_NORMAL_RUN;
+ frames_queued_ = -EPIPE;
+
+ // Assume device in normal run stream state;
+ cras_iodev_prepare_output_before_write_samples_state = \
+ CRAS_IODEV_STATE_NORMAL_RUN;
+
+ do_playback(thread_);
+
+ // 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);
+
+ thread_rm_open_dev(thread_, &iodev);
+ TearDownRstream(&rstream);
}
TEST(AUdioThreadStreams, DrainStream) {
@@ -376,7 +686,7 @@ int cras_iodev_add_stream(struct cras_iodev *iodev, struct dev_stream *stream)
unsigned int cras_iodev_all_streams_written(struct cras_iodev *iodev)
{
- return 0;
+ return cras_iodev_all_streams_written_ret;
}
int cras_iodev_close(struct cras_iodev *iodev)
@@ -445,7 +755,8 @@ void cras_iodev_stream_written(struct cras_iodev *iodev,
{
}
-int cras_iodev_update_rate(struct cras_iodev *iodev, unsigned int level)
+int cras_iodev_update_rate(struct cras_iodev *iodev, unsigned int level,
+ struct timespec *level_tstamp)
{
return 0;
}
@@ -458,6 +769,8 @@ int cras_iodev_put_input_buffer(struct cras_iodev *iodev, unsigned int nframes)
int cras_iodev_put_output_buffer(struct cras_iodev *iodev, uint8_t *frames,
unsigned int nframes)
{
+ cras_iodev_put_output_buffer_called++;
+ cras_iodev_put_output_buffer_nframes = nframes;
return 0;
}
@@ -472,6 +785,8 @@ int cras_iodev_get_output_buffer(struct cras_iodev *iodev,
struct cras_audio_area **area,
unsigned *frames)
{
+ cras_iodev_get_output_buffer_called++;
+ *area = cras_iodev_get_output_buffer_area;
return 0;
}
@@ -480,6 +795,17 @@ int cras_iodev_get_dsp_delay(const struct cras_iodev *iodev)
return 0;
}
+void cras_fmt_conv_destroy(struct cras_fmt_conv *conv)
+{
+}
+
+struct cras_fmt_conv *cras_channel_remix_conv_create(
+ unsigned int num_channels,
+ const float *coefficient)
+{
+ return NULL;
+}
+
void cras_rstream_dev_attach(struct cras_rstream *rstream,
unsigned int dev_id,
void *dev_ptr)
@@ -520,6 +846,11 @@ unsigned int cras_rstream_dev_offset(const struct cras_rstream *rstream,
return 0;
}
+void cras_rstream_record_fetch_interval(struct cras_rstream *rstream,
+ const struct timespec *now)
+{
+}
+
int cras_set_rt_scheduling(int rt_lim)
{
return 0;
@@ -537,7 +868,7 @@ void cras_system_rm_select_fd(int fd)
unsigned int dev_stream_capture(struct dev_stream *dev_stream,
const struct cras_audio_area *area,
unsigned int area_offset,
- unsigned int dev_index)
+ float software_gain_scaler)
{
return 0;
}
@@ -560,7 +891,7 @@ int dev_stream_capture_update_rstream(struct dev_stream *dev_stream)
struct dev_stream *dev_stream_create(struct cras_rstream *stream,
unsigned int dev_id,
const struct cras_audio_format *dev_fmt,
- void *dev_ptr)
+ void *dev_ptr, struct timespec *cb_ts)
{
struct dev_stream *out = static_cast<dev_stream*>(calloc(1, sizeof(*out)));
out->stream = stream;
@@ -582,7 +913,7 @@ int dev_stream_mix(struct dev_stream *dev_stream,
int dev_stream_playback_frames(const struct dev_stream *dev_stream)
{
- return 0;
+ return dev_stream_playback_frames_ret;
}
int dev_stream_playback_update_rstream(struct dev_stream *dev_stream)
@@ -623,14 +954,47 @@ void dev_stream_update_frames(const struct dev_stream *dev_stream)
{
}
-int cras_iodev_frames_queued(struct cras_iodev *iodev)
+int dev_stream_wake_time(struct dev_stream *dev_stream,
+ unsigned int curr_level,
+ struct timespec *level_tstamp,
+ struct timespec *wake_time)
+{
+ if (dev_stream_wake_time_val.find(dev_stream) !=
+ dev_stream_wake_time_val.end()) {
+ wake_time->tv_sec = dev_stream_wake_time_val[dev_stream].tv_sec;
+ wake_time->tv_nsec = dev_stream_wake_time_val[dev_stream].tv_nsec;
+ }
+ return 0;
+}
+
+int cras_iodev_frames_queued(struct cras_iodev *iodev, struct timespec *tstamp)
{
- return iodev->frames_queued(iodev);
+ return iodev->frames_queued(iodev, tstamp);
}
int cras_iodev_buffer_avail(struct cras_iodev *iodev, unsigned hw_level)
{
- return iodev->buffer_size - iodev->frames_queued(iodev);
+ struct timespec tstamp;
+ return iodev->buffer_size - iodev->frames_queued(iodev, &tstamp);
+}
+
+int cras_iodev_fill_odev_zeros(struct cras_iodev *odev, unsigned int frames)
+{
+ cras_iodev_fill_odev_zeros_frames = frames;
+ return 0;
+}
+
+int cras_iodev_output_underrun(struct cras_iodev *odev)
+{
+ cras_iodev_output_underrun_called++;
+ return 0;
+}
+
+int cras_iodev_prepare_output_before_write_samples(struct cras_iodev *odev)
+{
+ cras_iodev_prepare_output_before_write_samples_called++;
+ odev->state = cras_iodev_prepare_output_before_write_samples_state;
+ return cras_iodev_prepare_output_before_write_samples_ret;
}
int cras_server_metrics_longest_fetch_delay(int delay_msec)
@@ -638,6 +1002,68 @@ int cras_server_metrics_longest_fetch_delay(int delay_msec)
return 0;
}
+float cras_iodev_get_software_gain_scaler(const struct cras_iodev *iodev)
+{
+ return 1.0f;
+}
+
+unsigned int cras_iodev_frames_to_play_in_sleep(struct cras_iodev *odev,
+ unsigned int *hw_level,
+ struct timespec *hw_tstamp)
+{
+ clock_gettime(CLOCK_MONOTONIC_RAW, hw_tstamp);
+ *hw_level = 0;
+ return 0;
+}
+
+int cras_iodev_odev_should_wake(const struct cras_iodev *odev)
+{
+ return 1;
+}
+
+struct cras_audio_area *cras_audio_area_create(int num_channels)
+{
+ struct cras_audio_area *area;
+ size_t sz;
+
+ sz = sizeof(*area) + num_channels * sizeof(struct cras_channel_area);
+ area = (cras_audio_area *)calloc(1, sz);
+ area->num_channels = num_channels;
+ area->channels[0].buf = (uint8_t*)calloc(1, BUFFER_SIZE * 2 * num_channels);
+
+ return area;
+}
+
+enum CRAS_IODEV_STATE cras_iodev_state(const struct cras_iodev *iodev)
+{
+ return iodev->state;
+}
+
+unsigned int cras_iodev_get_num_underruns(const struct cras_iodev *iodev)
+{
+ return 0;
+}
+
+int cras_iodev_reset_request(struct cras_iodev *iodev)
+{
+ cras_iodev_reset_request_called++;
+ cras_iodev_reset_request_iodev = iodev;
+ return 0;
+}
+
+unsigned int cras_iodev_get_num_severe_underruns(const struct cras_iodev *iodev)
+{
+ return 0;
+}
+
+int cras_iodev_start_ramp(struct cras_iodev *odev,
+ enum CRAS_IODEV_RAMP_REQUEST request)
+{
+ cras_iodev_start_ramp_odev = odev;
+ cras_iodev_start_ramp_request = request;
+ return 0;
+}
+
} // extern "C"
int main(int argc, char **argv) {
diff --git a/cras/src/tests/bt_device_unittest.cc b/cras/src/tests/bt_device_unittest.cc
index 9f66b329..4f7cd86c 100644
--- a/cras/src/tests/bt_device_unittest.cc
+++ b/cras/src/tests/bt_device_unittest.cc
@@ -44,26 +44,20 @@ class BtDeviceTestSuite : public testing::Test {
virtual void SetUp() {
ResetStubData();
bt_iodev1.direction = CRAS_STREAM_OUTPUT;
- bt_iodev1.is_open = is_open;
bt_iodev1.update_active_node = update_active_node;
bt_iodev2.direction = CRAS_STREAM_INPUT;
- bt_iodev2.is_open = is_open;
bt_iodev2.update_active_node = update_active_node;
d1_.direction = CRAS_STREAM_OUTPUT;
- d1_.is_open = is_open;
d1_.update_active_node = update_active_node;
d2_.direction = CRAS_STREAM_OUTPUT;
- d2_.is_open = is_open;
d2_.update_active_node = update_active_node;
d3_.direction = CRAS_STREAM_INPUT;
- d3_.is_open = is_open;
d3_.update_active_node = update_active_node;
}
- static int is_open(const cras_iodev* iodev) {
- return is_open_;
- }
+
static void update_active_node(struct cras_iodev *iodev,
- unsigned node_idx) {
+ unsigned node_idx,
+ unsigned dev_enabled) {
}
struct cras_iodev bt_iodev1;
@@ -71,15 +65,12 @@ class BtDeviceTestSuite : public testing::Test {
struct cras_iodev d3_;
struct cras_iodev d2_;
struct cras_iodev d1_;
- static int is_open_;
};
-int BtDeviceTestSuite::is_open_;
-
TEST(BtDeviceSuite, CreateBtDevice) {
struct cras_bt_device *device;
- device = cras_bt_device_create(FAKE_OBJ_PATH);
+ device = cras_bt_device_create(NULL, FAKE_OBJ_PATH);
EXPECT_NE((void *)NULL, device);
device = cras_bt_device_get(FAKE_OBJ_PATH);
@@ -92,7 +83,7 @@ TEST(BtDeviceSuite, CreateBtDevice) {
TEST_F(BtDeviceTestSuite, AppendRmIodev) {
struct cras_bt_device *device;
- device = cras_bt_device_create(FAKE_OBJ_PATH);
+ device = cras_bt_device_create(NULL, FAKE_OBJ_PATH);
bt_iodev1.nodes = reinterpret_cast<struct cras_ionode*>(0x123);
cras_bt_io_create_profile_ret = &bt_iodev1;
cras_bt_device_append_iodev(device, &d1_,
@@ -132,7 +123,7 @@ TEST_F(BtDeviceTestSuite, SwitchProfile) {
struct cras_bt_device *device;
ResetStubData();
- device = cras_bt_device_create(FAKE_OBJ_PATH);
+ device = cras_bt_device_create(NULL, FAKE_OBJ_PATH);
cras_bt_io_create_profile_ret = &bt_iodev1;
cras_bt_device_append_iodev(device, &d1_,
CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE);
@@ -141,7 +132,7 @@ TEST_F(BtDeviceTestSuite, SwitchProfile) {
CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY);
cras_bt_device_start_monitor();
- cras_bt_device_switch_profile_on_open(device, &bt_iodev1);
+ cras_bt_device_switch_profile_enable_dev(device, &bt_iodev1);
/* Two bt iodevs were all active. */
cras_main_message_add_handler_callback(
@@ -149,13 +140,13 @@ TEST_F(BtDeviceTestSuite, SwitchProfile) {
cras_main_message_add_handler_callback_data);
/* One bt iodev was active, the other was not. */
- cras_bt_device_switch_profile_on_open(device, &bt_iodev2);
+ cras_bt_device_switch_profile_enable_dev(device, &bt_iodev2);
cras_main_message_add_handler_callback(
cras_main_message_send_msg,
cras_main_message_add_handler_callback_data);
/* Output bt iodev wasn't active, close the active input iodev. */
- cras_bt_device_switch_profile_on_close(device, &bt_iodev2);
+ cras_bt_device_switch_profile(device, &bt_iodev2);
cras_main_message_add_handler_callback(
cras_main_message_send_msg,
cras_main_message_add_handler_callback_data);
@@ -240,6 +231,27 @@ struct hfp_slc_handle *cras_hfp_ag_get_slc(struct cras_bt_device *device)
return NULL;
}
+void cras_hfp_ag_suspend_connected_device(struct cras_bt_device *device)
+{
+}
+
+void cras_a2dp_suspend_connected_device(struct cras_bt_device *device)
+{
+}
+
+void cras_a2dp_start(struct cras_bt_device *device)
+{
+}
+
+int cras_hfp_ag_start(struct cras_bt_device *device)
+{
+ return 0;
+}
+
+void cras_hfp_ag_suspend()
+{
+}
+
/* From hfp_slc */
int hfp_event_speaker_gain(struct hfp_slc_handle *handle, int gain)
{
@@ -256,7 +268,7 @@ int cras_iodev_close(struct cras_iodev *dev) {
return 0;
}
-int cras_iodev_list_dev_is_enabled(struct cras_iodev *dev)
+int cras_iodev_list_dev_is_enabled(const struct cras_iodev *dev)
{
return 0;
}
@@ -269,6 +281,10 @@ void cras_iodev_list_enable_dev(struct cras_iodev *dev)
{
}
+void cras_iodev_list_notify_node_volume(struct cras_ionode *node)
+{
+}
+
int cras_main_message_send(struct cras_main_message *msg)
{
cras_main_message_send_msg = msg;
@@ -291,6 +307,15 @@ struct cras_tm *cras_system_state_get_tm()
}
/* From cras_tm */
+struct cras_timer *cras_tm_create_timer(
+ struct cras_tm *tm,
+ unsigned int ms,
+ void (*cb)(struct cras_timer *t, void *data),
+ void *cb_data)
+{
+ return NULL;
+}
+
void cras_tm_cancel_timer(struct cras_tm *tm, struct cras_timer *t)
{
}
diff --git a/cras/src/tests/bt_io_unittest.cc b/cras/src/tests/bt_io_unittest.cc
index ad876273..979404ac 100644
--- a/cras/src/tests/bt_io_unittest.cc
+++ b/cras/src/tests/bt_io_unittest.cc
@@ -23,8 +23,8 @@ static unsigned int cras_iodev_list_rm_input_called;
static unsigned int cras_bt_device_set_active_profile_called;
static unsigned int cras_bt_device_set_active_profile_val;
static int cras_bt_device_get_active_profile_ret;
-static int cras_bt_device_switch_profile_on_open_called;
-static int cras_bt_device_switch_profile_on_close_called;
+static int cras_bt_device_switch_profile_enable_dev_called;
+static int cras_bt_device_switch_profile_called;
static int cras_bt_device_can_switch_to_a2dp_ret;
static int cras_bt_device_has_a2dp_ret;
static int is_utf8_string_ret_value;
@@ -41,8 +41,8 @@ void ResetStubData() {
cras_bt_device_set_active_profile_called = 0;
cras_bt_device_set_active_profile_val = 0;
cras_bt_device_get_active_profile_ret = 0;
- cras_bt_device_switch_profile_on_open_called= 0;
- cras_bt_device_switch_profile_on_close_called = 0;
+ cras_bt_device_switch_profile_enable_dev_called= 0;
+ cras_bt_device_switch_profile_called = 0;
cras_bt_device_can_switch_to_a2dp_ret = 0;
cras_bt_device_has_a2dp_ret = 0;
is_utf8_string_ret_value = 1;
@@ -62,10 +62,8 @@ class BtIoBasicSuite : public testing::Test {
delay_frames_called_ = 0;
get_buffer_called_ = 0;
put_buffer_called_ = 0;
- is_open_called_ = 0;
open_dev_called_ = 0;
close_dev_called_ = 0;
- dev_running_called_ = 0;
}
virtual void TearDown() {
@@ -79,10 +77,8 @@ class BtIoBasicSuite : public testing::Test {
d->delay_frames = delay_frames;
d->get_buffer = get_buffer;
d->put_buffer = put_buffer;
- d->is_open = is_open;
d->open_dev = open_dev;
d->close_dev = close_dev;
- d->dev_running = dev_running;
}
// Stub functions for the iodev structure.
@@ -102,7 +98,8 @@ class BtIoBasicSuite : public testing::Test {
update_supported_formats_called_++;
return 0;
}
- static int frames_queued(const cras_iodev* iodev) {
+ static int frames_queued(const cras_iodev* iodev,
+ struct timespec *tstamp) {
frames_queued_called_++;
return 0;
}
@@ -121,10 +118,6 @@ class BtIoBasicSuite : public testing::Test {
put_buffer_called_++;
return 0;
}
- static int is_open(const cras_iodev* iodev) {
- is_open_called_++;
- return 0;
- }
static int open_dev(cras_iodev* iodev) {
open_dev_called_++;
return 0;
@@ -133,10 +126,6 @@ class BtIoBasicSuite : public testing::Test {
close_dev_called_++;
return 0;
}
- static int dev_running(const cras_iodev* iodev) {
- dev_running_called_++;
- return 1;
- }
static struct cras_iodev *bt_iodev;
static struct cras_iodev iodev_;
@@ -146,10 +135,8 @@ class BtIoBasicSuite : public testing::Test {
static unsigned int delay_frames_called_;
static unsigned int get_buffer_called_;
static unsigned int put_buffer_called_;
- static unsigned int is_open_called_;
static unsigned int open_dev_called_;
static unsigned int close_dev_called_;
- static unsigned int dev_running_called_;
};
struct cras_iodev *BtIoBasicSuite::bt_iodev;
@@ -160,14 +147,13 @@ unsigned int BtIoBasicSuite::frames_queued_called_;
unsigned int BtIoBasicSuite::delay_frames_called_;
unsigned int BtIoBasicSuite::get_buffer_called_;
unsigned int BtIoBasicSuite::put_buffer_called_;
-unsigned int BtIoBasicSuite::is_open_called_;
unsigned int BtIoBasicSuite::open_dev_called_;
unsigned int BtIoBasicSuite::close_dev_called_;
-unsigned int BtIoBasicSuite::dev_running_called_;
TEST_F(BtIoBasicSuite, CreateBtIo) {
struct cras_audio_area *fake_area;
struct cras_audio_format fake_fmt;
+ struct timespec tstamp;
unsigned fr;
bt_iodev = cras_bt_io_create(fake_device, &iodev_,
CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE);
@@ -180,16 +166,12 @@ TEST_F(BtIoBasicSuite, CreateBtIo) {
bt_iodev->open_dev(bt_iodev);
EXPECT_EQ(1, open_dev_called_);
- bt_iodev->is_open(bt_iodev);
- EXPECT_EQ(1, is_open_called_);
- bt_iodev->frames_queued(bt_iodev);
+ bt_iodev->frames_queued(bt_iodev, &tstamp);
EXPECT_EQ(1, frames_queued_called_);
bt_iodev->get_buffer(bt_iodev, &fake_area, &fr);
EXPECT_EQ(1, get_buffer_called_);
bt_iodev->put_buffer(bt_iodev, fr);
EXPECT_EQ(1, put_buffer_called_);
- bt_iodev->dev_running(bt_iodev);
- EXPECT_EQ(1, dev_running_called_);
bt_iodev->close_dev(bt_iodev);
EXPECT_EQ(1, close_dev_called_);
EXPECT_EQ(1, cras_iodev_free_format_called);
@@ -209,7 +191,7 @@ TEST_F(BtIoBasicSuite, SwitchProfileOnUpdateFormatForInputDev) {
EXPECT_EQ(CRAS_BT_DEVICE_PROFILE_HSP_AUDIOGATEWAY |
CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY,
cras_bt_device_set_active_profile_val);
- EXPECT_EQ(1, cras_bt_device_switch_profile_on_open_called);
+ EXPECT_EQ(1, cras_bt_device_switch_profile_enable_dev_called);
}
TEST_F(BtIoBasicSuite, NoSwitchProfileOnUpdateFormatForInputDevAlreadyOnHfp) {
@@ -223,7 +205,7 @@ TEST_F(BtIoBasicSuite, NoSwitchProfileOnUpdateFormatForInputDevAlreadyOnHfp) {
CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY;
bt_iodev->update_supported_formats(bt_iodev);
- EXPECT_EQ(0, cras_bt_device_switch_profile_on_open_called);
+ EXPECT_EQ(0, cras_bt_device_switch_profile_enable_dev_called);
}
TEST_F(BtIoBasicSuite, SwitchProfileOnCloseInputDev) {
@@ -240,7 +222,7 @@ TEST_F(BtIoBasicSuite, SwitchProfileOnCloseInputDev) {
EXPECT_EQ(CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE,
cras_bt_device_set_active_profile_val);
- EXPECT_EQ(1, cras_bt_device_switch_profile_on_close_called);
+ EXPECT_EQ(1, cras_bt_device_switch_profile_called);
}
TEST_F(BtIoBasicSuite, NoSwitchProfileOnCloseInputDevNoSupportA2dp) {
@@ -255,7 +237,7 @@ TEST_F(BtIoBasicSuite, NoSwitchProfileOnCloseInputDevNoSupportA2dp) {
cras_bt_device_has_a2dp_ret = 0;
bt_iodev->close_dev(bt_iodev);
- EXPECT_EQ(0, cras_bt_device_switch_profile_on_close_called);
+ EXPECT_EQ(0, cras_bt_device_switch_profile_called);
}
TEST_F(BtIoBasicSuite, SwitchProfileOnAppendA2dpDev) {
@@ -269,7 +251,8 @@ TEST_F(BtIoBasicSuite, SwitchProfileOnAppendA2dpDev) {
EXPECT_EQ(CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE,
cras_bt_device_set_active_profile_val);
- EXPECT_EQ(1, cras_bt_device_switch_profile_on_open_called);
+ EXPECT_EQ(0, cras_bt_device_switch_profile_enable_dev_called);
+ EXPECT_EQ(1, cras_bt_device_switch_profile_called);
}
TEST_F(BtIoBasicSuite, NoSwitchProfileOnAppendHfpDev) {
@@ -281,7 +264,7 @@ TEST_F(BtIoBasicSuite, NoSwitchProfileOnAppendHfpDev) {
cras_bt_io_append(bt_iodev, &iodev2_,
CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY);
- EXPECT_EQ(0, cras_bt_device_switch_profile_on_open_called);
+ EXPECT_EQ(0, cras_bt_device_switch_profile_enable_dev_called);
}
TEST_F(BtIoBasicSuite, CreateSetDeviceActiveProfileToA2DP) {
@@ -422,22 +405,33 @@ int cras_bt_device_can_switch_to_a2dp(struct cras_bt_device *device)
return cras_bt_device_can_switch_to_a2dp_ret;
}
-int cras_bt_device_switch_profile_on_close(struct cras_bt_device *device,
+int cras_bt_device_switch_profile(struct cras_bt_device *device,
struct cras_iodev *bt_iodev)
{
- cras_bt_device_switch_profile_on_close_called++;
+ cras_bt_device_switch_profile_called++;
return 0;
}
-int cras_bt_device_switch_profile_on_open(struct cras_bt_device *device,
+int cras_bt_device_switch_profile_enable_dev(struct cras_bt_device *device,
struct cras_iodev *bt_iodev)
{
- cras_bt_device_switch_profile_on_open_called++;
+ cras_bt_device_switch_profile_enable_dev_called++;
return 0;
}
+const char *cras_bt_device_object_path(const struct cras_bt_device *device)
+{
+ return "/fake/object/path";
+}
+
int is_utf8_string(const char* string)
{
return is_utf8_string_ret_value;
}
+
+int cras_iodev_default_no_stream_playback(struct cras_iodev *odev, int enable)
+{
+ return 0;
+}
+
} // extern "C"
diff --git a/cras/src/tests/bt_profile_unittest.cc b/cras/src/tests/bt_profile_unittest.cc
index 8598e39e..103d3ce5 100644
--- a/cras/src/tests/bt_profile_unittest.cc
+++ b/cras/src/tests/bt_profile_unittest.cc
@@ -29,7 +29,6 @@ static int profile_cancel_called;
static struct cras_bt_profile *profile_cancel_arg_value;
static int cras_bt_transport_get_called;
static const char *cras_bt_transport_get_arg_value;
-static int cras_bt_transport_fill_properties_called;
void fake_profile_release(struct cras_bt_profile *profile);
void fake_profile_new_connection(struct cras_bt_profile *profile,
@@ -59,7 +58,6 @@ class BtProfileTestSuite : public DBusTest {
fake_profile.cancel = fake_profile_cancel;
fake_transport = reinterpret_cast<struct cras_bt_transport*>(0x321);
- cras_bt_transport_fill_properties_called = 0;
cras_bt_transport_get_called = 0;
}
};
@@ -109,7 +107,6 @@ TEST_F(BtProfileTestSuite, HandleMessage) {
ASSERT_EQ(1, profile_new_connection_called);
ASSERT_STREQ("device", cras_bt_transport_get_arg_value);
ASSERT_EQ(1, cras_bt_transport_get_called);
- ASSERT_EQ(1, cras_bt_transport_fill_properties_called);
ASSERT_EQ(fake_transport, profile_new_connection_arg_value);
CreateMessageCall("/fake", "org.bluez.Profile1", "RequestDisconnection")
@@ -186,13 +183,6 @@ struct cras_bt_transport *cras_bt_transport_create(DBusConnection *conn,
return fake_transport;
}
-void cras_bt_transport_fill_properties(struct cras_bt_transport *transport,
- int fd, const char *uuid)
-{
- cras_bt_transport_fill_properties_called++;
- /* Close the duplicated fd */
- close(fd);
-}
} // extern "C"
int main(int argc, char **argv) {
diff --git a/cras/src/tests/card_config_unittest.cc b/cras/src/tests/card_config_unittest.cc
index 184e0859..e4bed40e 100644
--- a/cras/src/tests/card_config_unittest.cc
+++ b/cras/src/tests/card_config_unittest.cc
@@ -22,7 +22,7 @@ static unsigned int cras_volume_curve_create_explicit_called;
static long cras_explicit_curve[101];
static struct cras_volume_curve* cras_volume_curve_create_explicit_return;
-static const char CONFIG_PATH[] = "/tmp";
+static const char CONFIG_PATH[] = CRAS_UT_TMPDIR;
void CreateConfigFile(const char* name, const char* config_text) {
FILE* f;
@@ -63,9 +63,8 @@ TEST_F(CardConfigTestSuite, NoConfigFound) {
EXPECT_EQ(NULL, config);
}
-// Test an empty config file, should return a config, but give back the default
-// volume curve.
-TEST_F(CardConfigTestSuite, EmptyConfigFileReturnsValidConfigDefaultCurves) {
+// Test an empty config file, should return a null volume curve.
+TEST_F(CardConfigTestSuite, EmptyConfigFileReturnsNullVolumeCurve) {
static const char empty_config_text[] = "";
static const char empty_config_name[] = "EmptyConfigCard";
struct cras_card_config* config;
@@ -77,19 +76,19 @@ TEST_F(CardConfigTestSuite, EmptyConfigFileReturnsValidConfigDefaultCurves) {
EXPECT_NE(static_cast<struct cras_card_config*>(NULL), config);
curve = cras_card_config_get_volume_curve_for_control(config, "asdf");
- EXPECT_EQ(1, cras_volume_curve_create_default_called);
- EXPECT_NE(static_cast<struct cras_volume_curve*>(NULL), curve);
+ EXPECT_EQ(0, cras_volume_curve_create_default_called);
+ EXPECT_EQ(static_cast<struct cras_volume_curve*>(NULL), curve);
cras_card_config_destroy(config);
}
-// Getting a curve from a null config should return a default curve.
+// Getting a curve from a null config should always return null volume curve.
TEST_F(CardConfigTestSuite, NullConfigGivesDefaultVolumeCurve) {
struct cras_volume_curve* curve;
curve = cras_card_config_get_volume_curve_for_control(NULL, "asdf");
- EXPECT_EQ(1, cras_volume_curve_create_default_called);
- EXPECT_NE(static_cast<struct cras_volume_curve*>(NULL), curve);
+ EXPECT_EQ(0, cras_volume_curve_create_default_called);
+ EXPECT_EQ(static_cast<struct cras_volume_curve*>(NULL), curve);
}
// Test getting a curve from a simple_step configuration.
@@ -110,9 +109,8 @@ TEST_F(CardConfigTestSuite, SimpleStepConfig) {
// Unknown config should return default curve.
curve = cras_card_config_get_volume_curve_for_control(NULL, "asdf");
- EXPECT_EQ(1, cras_volume_curve_create_default_called);
- EXPECT_EQ(cras_volume_curve_create_default_return, curve);
- cras_volume_curve_create_default_called = 0;
+ EXPECT_EQ(0, cras_volume_curve_create_default_called);
+ EXPECT_EQ(static_cast<struct cras_volume_curve*>(NULL), curve);
// Test a config that specifies simple_step.
curve = cras_card_config_get_volume_curve_for_control(config, "Card1");
diff --git a/cras/src/tests/cras_client_unittest.cc b/cras/src/tests/cras_client_unittest.cc
index 437c77bf..1853ae43 100644
--- a/cras/src/tests/cras_client_unittest.cc
+++ b/cras/src/tests/cras_client_unittest.cc
@@ -3,7 +3,6 @@
// found in the LICENSE file.
#include <stdio.h>
-#include <sys/shm.h>
#include <gtest/gtest.h>
extern "C" {
@@ -15,33 +14,36 @@ extern "C" {
static const cras_stream_id_t FIRST_STREAM_ID = 1;
-static int shmat_called;
-static int shmdt_called;
-static int shmget_called;
static int pthread_create_called;
static int pthread_join_called;
+static int pthread_cond_timedwait_called;
+static int pthread_cond_timedwait_retval;
static int close_called;
static int pipe_called;
static int sendmsg_called;
static int write_called;
+static void *mmap_return_value;
+static int samples_ready_called;
+static int samples_ready_frames_value;
+static uint8_t *samples_ready_samples_value;
-static void* shmat_returned_value;
static int pthread_create_returned_value;
namespace {
void InitStaticVariables() {
- shmat_called = 0;
- shmdt_called = 0;
- shmget_called = 0;
pthread_create_called = 0;
pthread_join_called = 0;
+ pthread_cond_timedwait_called = 0;
+ pthread_cond_timedwait_retval = 0;
close_called = 0;
pipe_called = 0;
sendmsg_called = 0;
write_called = 0;
- shmat_returned_value = NULL;
pthread_create_returned_value = 0;
+ mmap_return_value = NULL;
+ samples_ready_called = 0;
+ samples_ready_frames_value = 0;
}
class CrasClientTestSuite : public testing::Test {
@@ -67,6 +69,7 @@ class CrasClientTestSuite : public testing::Test {
InitStaticVariables();
memset(&client_, 0, sizeof(client_));
+ client_.server_fd_state = CRAS_SOCKET_STATE_CONNECTED;
memset(&stream_, 0, sizeof(stream_));
stream_.id = FIRST_STREAM_ID;
@@ -104,10 +107,68 @@ void set_audio_format(struct cras_audio_format* format,
format->channel_layout[i] = i < num_channels ? i : -1;
}
+int capture_samples_ready(cras_client* client,
+ cras_stream_id_t stream_id,
+ uint8_t* samples,
+ size_t frames,
+ const timespec* sample_ts,
+ void* arg) {
+ samples_ready_called++;
+ samples_ready_samples_value = samples;
+ samples_ready_frames_value = frames;
+ return frames;
+}
+
+TEST_F(CrasClientTestSuite, HandleCaptureDataReady) {
+ struct cras_audio_shm *shm = &stream_.capture_shm;
+
+ stream_.direction = CRAS_STREAM_INPUT;
+
+ shm_writable_frames_ = 480;
+ InitShm(shm);
+ stream_.config->buffer_frames = 480;
+ stream_.config->cb_threshold = 480;
+ stream_.config->aud_cb = capture_samples_ready;
+ stream_.config->unified_cb = 0;
+
+ shm->area->write_buf_idx = 0;
+ shm->area->read_buf_idx = 0;
+ shm->area->write_offset[0] = 480 * 4;
+ shm->area->read_offset[0] = 0;
+
+ /* Normal scenario: read buffer has full of data written,
+ * handle_capture_data_ready() should consume all 480 frames and move
+ * read_buf_idx to the next buffer. */
+ handle_capture_data_ready(&stream_, 480);
+ EXPECT_EQ(1, samples_ready_called);
+ EXPECT_EQ(480, samples_ready_frames_value);
+ EXPECT_EQ(cras_shm_buff_for_idx(shm, 0), samples_ready_samples_value);
+ EXPECT_EQ(1, shm->area->read_buf_idx);
+ EXPECT_EQ(0, shm->area->write_offset[0]);
+ EXPECT_EQ(0, shm->area->read_offset[0]);
+
+ /* At the beginning of overrun: handle_capture_data_ready() should not
+ * proceed to call audio_cb because there's no data captured. */
+ shm->area->read_buf_idx = 0;
+ shm->area->write_offset[0] = 0;
+ shm->area->read_offset[0] = 0;
+ handle_capture_data_ready(&stream_, 480);
+ EXPECT_EQ(1, samples_ready_called);
+ EXPECT_EQ(0, shm->area->read_buf_idx);
+
+ /* In the middle of overrun: partially written buffer should trigger
+ * audio_cb, feed the full-sized read buffer to client. */
+ shm->area->read_buf_idx = 0;
+ shm->area->write_offset[0] = 123;
+ shm->area->read_offset[0] = 0;
+ handle_capture_data_ready(&stream_, 480);
+ EXPECT_EQ(1, samples_ready_called);
+ EXPECT_EQ(0, shm->area->read_buf_idx);
+}
+
void CrasClientTestSuite::StreamConnected(CRAS_STREAM_DIRECTION direction) {
struct cras_client_stream_connected msg;
- int input_shm_key = 0;
- int output_shm_key = 1;
+ int shm_fds[2] = {0, 1};
int shm_max_size = 600;
size_t format_bytes;
struct cras_audio_shm_area area;
@@ -124,22 +185,18 @@ void CrasClientTestSuite::StreamConnected(CRAS_STREAM_DIRECTION direction) {
area.config.frame_bytes = format_bytes;
area.config.used_size = shm_writable_frames_ * format_bytes;
- shmat_returned_value = &area;
+ mmap_return_value = &area;
cras_fill_client_stream_connected(
&msg,
0,
stream_.id,
&server_format,
- input_shm_key,
- output_shm_key,
shm_max_size);
- stream_connected(&stream_, &msg);
+ stream_connected(&stream_, &msg, shm_fds, 2);
- EXPECT_EQ(1, shmget_called);
- EXPECT_EQ(1, shmat_called);
- EXPECT_NE(0, stream_.thread.running);
+ EXPECT_EQ(CRAS_THREAD_RUNNING, stream_.thread.state);
if (direction == CRAS_STREAM_OUTPUT) {
EXPECT_EQ(NULL, stream_.capture_shm.area);
@@ -162,11 +219,11 @@ void CrasClientTestSuite::StreamConnectedFail(
CRAS_STREAM_DIRECTION direction) {
struct cras_client_stream_connected msg;
- int input_shm_key = 0;
- int output_shm_key = 1;
+ int shm_fds[2] = {0, 1};
int shm_max_size = 600;
size_t format_bytes;
struct cras_audio_shm_area area;
+ int rc;
stream_.direction = direction;
set_audio_format(&stream_.config->format, SND_PCM_FORMAT_S16_LE, 48000, 4);
@@ -174,34 +231,31 @@ void CrasClientTestSuite::StreamConnectedFail(
struct cras_audio_format server_format;
set_audio_format(&server_format, SND_PCM_FORMAT_S16_LE, 44100, 2);
+ // Thread setup
+ rc = pipe(stream_.wake_fds);
+ ASSERT_EQ(0, rc);
+ stream_.thread.state = CRAS_THREAD_WARMUP;
+
// Initialize shm area
format_bytes = cras_get_format_bytes(&server_format);
memset(&area, 0, sizeof(area));
area.config.frame_bytes = format_bytes;
area.config.used_size = shm_writable_frames_ * format_bytes;
- shmat_returned_value = &area;
-
- // let pthread_create fail
- pthread_create_returned_value = -1;
+ mmap_return_value = &area;
+ // Put an error in the message.
cras_fill_client_stream_connected(
&msg,
- 0,
+ 1,
stream_.id,
&server_format,
- input_shm_key,
- output_shm_key,
shm_max_size);
- stream_connected(&stream_, &msg);
+ stream_connected(&stream_, &msg, shm_fds, 2);
- EXPECT_EQ(0, stream_.thread.running);
- EXPECT_EQ(1, shmget_called);
- EXPECT_EQ(1, shmat_called);
- EXPECT_EQ(1, shmdt_called);
- EXPECT_EQ(1, pipe_called);
- EXPECT_EQ(2, close_called); // close the pipefds
+ EXPECT_EQ(CRAS_THREAD_STOP, stream_.thread.state);
+ EXPECT_EQ(4, close_called); // close the pipefds and shm_fds
}
TEST_F(CrasClientTestSuite, InputStreamConnectedFail) {
@@ -223,14 +277,23 @@ TEST_F(CrasClientTestSuite, AddAndRemoveStream) {
malloc(sizeof(*(stream_ptr->config)));
memcpy(stream_ptr->config, stream_.config, sizeof(*(stream_.config)));
+ pthread_cond_timedwait_retval = ETIMEDOUT;
+ EXPECT_EQ(-ETIMEDOUT, client_thread_add_stream(
+ &client_, stream_ptr, &stream_id, NO_DEVICE));
+ EXPECT_EQ(pthread_cond_timedwait_called, 1);
+ EXPECT_EQ(pthread_join_called, 0);
+
+ InitStaticVariables();
EXPECT_EQ(0, client_thread_add_stream(
&client_, stream_ptr, &stream_id, NO_DEVICE));
EXPECT_EQ(&client_, stream_ptr->client);
EXPECT_EQ(stream_id, stream_ptr->id);
+ EXPECT_EQ(pthread_create_called, 1);
+ EXPECT_EQ(pipe_called, 1);
EXPECT_EQ(1, sendmsg_called); // send connect message to server
EXPECT_EQ(stream_ptr, stream_from_id(&client_, stream_id));
- stream_ptr->thread.running = 1;
+ stream_ptr->thread.state = CRAS_THREAD_RUNNING;
EXPECT_EQ(0, client_thread_rm_stream(&client_, stream_id));
@@ -252,21 +315,6 @@ int main(int argc, char **argv) {
/* stubs */
extern "C" {
-int shmget(key_t key, size_t size, int shmflg) {
- ++shmget_called;
- return 0;
-}
-
-void* shmat(int shmid, const void* shmaddr, int shmflg) {
- ++shmat_called;
- return shmat_returned_value;
-}
-
-int shmdt(const void *shmaddr) {
- ++shmdt_called;
- return 0;
-}
-
ssize_t write(int fd, const void *buf, size_t count) {
++write_called;
return count;
@@ -302,9 +350,21 @@ int pthread_join(pthread_t thread, void **retval) {
return 0;
}
+int pthread_cond_timedwait(pthread_cond_t *__restrict cond,
+ pthread_mutex_t *__restrict mutex,
+ const struct timespec *__restrict timeout) {
+ ++pthread_cond_timedwait_called;
+ return pthread_cond_timedwait_retval;
+}
+
int clock_gettime(clockid_t clk_id, struct timespec *tp) {
tp->tv_sec = 0;
tp->tv_nsec = 0;
return 0;
}
+
+void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
+{
+ return mmap_return_value;
+}
}
diff --git a/cras/src/tests/cras_dsp_pipeline_unittest.cc b/cras/src/tests/cras_dsp_pipeline_unittest.cc
index 62d523c6..ef04cb66 100644
--- a/cras/src/tests/cras_dsp_pipeline_unittest.cc
+++ b/cras/src/tests/cras_dsp_pipeline_unittest.cc
@@ -411,6 +411,7 @@ TEST_F(DspPipelineTestSuite, Complex) {
struct cras_expr_env env = CRAS_EXPR_ENV_INIT;
cras_expr_env_install_builtins(&env);
cras_expr_env_set_variable_string(&env, "output_device", "HDMI");
+ cras_expr_env_set_variable_boolean(&env, "swap_lr_disabled", 1);
struct ini *ini = cras_dsp_ini_create(filename);
ASSERT_TRUE(ini);
diff --git a/cras/src/tests/cras_monitor.c b/cras/src/tests/cras_monitor.c
new file mode 100644
index 00000000..f09b9f88
--- /dev/null
+++ b/cras/src/tests/cras_monitor.c
@@ -0,0 +1,319 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/select.h>
+#include <unistd.h>
+
+#include "cras_client.h"
+#include "cras_types.h"
+#include "cras_util.h"
+#include "cras_version.h"
+
+static void output_volume_changed(void *context, int32_t volume)
+{
+ printf("output volume: %d/100\n", volume);
+}
+
+static void output_mute_changed(void *context, int muted,
+ int user_muted, int mute_locked)
+{
+ printf("output mute: muted: %d, user_muted: %d, mute_locked: %d\n",
+ muted, user_muted, mute_locked);
+}
+
+static void capture_gain_changed(void *context, int32_t gain)
+{
+ printf("capture gain: %d\n", gain);
+}
+
+static void capture_mute_changed(void *context, int muted, int mute_locked)
+{
+ printf("capture mute: muted: %d, mute_locked: %d\n",
+ muted, mute_locked);
+}
+
+static void nodes_changed(void *context)
+{
+ printf("nodes changed\n");
+}
+
+static const char *string_for_direction(enum CRAS_STREAM_DIRECTION dir)
+{
+ switch(dir) {
+ case CRAS_STREAM_OUTPUT:
+ return "output";
+ case CRAS_STREAM_INPUT:
+ return "input";
+ case CRAS_STREAM_POST_MIX_PRE_DSP:
+ return "post_mix_pre_dsp";
+ default:
+ break;
+ }
+
+ return "undefined";
+}
+
+size_t node_array_index_of_node_id(struct cras_ionode_info *nodes,
+ size_t num_nodes,
+ cras_node_id_t node_id)
+{
+ uint32_t dev_index = dev_index_of(node_id);
+ uint32_t node_index = node_index_of(node_id);
+ size_t i;
+
+ for (i = 0; i < num_nodes; i++) {
+ if (nodes[i].iodev_idx == dev_index &&
+ nodes[i].ionode_idx == node_index)
+ return i;
+ }
+ return CRAS_MAX_IONODES;
+}
+
+const char *node_name_for_node_id(struct cras_client *client,
+ enum CRAS_STREAM_DIRECTION dir,
+ cras_node_id_t node_id)
+{
+ struct cras_ionode_info nodes[CRAS_MAX_IONODES];
+ struct cras_iodev_info devs[CRAS_MAX_IODEVS];
+ size_t num_devs = CRAS_MAX_IODEVS;
+ size_t num_nodes = CRAS_MAX_IONODES;
+ uint32_t iodev_idx = dev_index_of(node_id);
+ size_t node_index;
+ char buf[1024];
+ int rc;
+
+ if (node_id == 0) {
+ return strdup("none");
+ } else if (iodev_idx <= 2) {
+ return strdup("fallback");
+ } else if (dir == CRAS_STREAM_POST_MIX_PRE_DSP) {
+ snprintf(buf, sizeof(buf), "%s node: %" PRIu64 "\n",
+ string_for_direction(dir), node_id);
+ return strdup(buf);
+ } else if (dir == CRAS_STREAM_OUTPUT) {
+ rc = cras_client_get_output_devices(client, devs, nodes,
+ &num_devs, &num_nodes);
+ } else if (dir == CRAS_STREAM_INPUT) {
+ rc = cras_client_get_input_devices(client, devs, nodes,
+ &num_devs, &num_nodes);
+ } else {
+ return strdup("unknown");
+ }
+
+ if (rc != 0) {
+ syslog(LOG_ERR, "Couldn't get output devices: %s\n",
+ strerror(-rc));
+ snprintf(buf, sizeof(buf), "%u:%u",
+ iodev_idx, node_index_of(node_id));
+ return strdup(buf);
+ }
+ node_index = node_array_index_of_node_id(nodes, num_nodes, node_id);
+ if (node_index >= num_nodes)
+ snprintf(buf, sizeof(buf),
+ "unknown: %zu >= %zu", node_index, num_nodes);
+ else
+ snprintf(buf, sizeof(buf), "%u:%u: %s",
+ nodes[node_index].iodev_idx,
+ nodes[node_index].ionode_idx,
+ nodes[node_index].name);
+ return strdup(buf);
+}
+
+static void active_node_changed(void *context,
+ enum CRAS_STREAM_DIRECTION dir,
+ cras_node_id_t node_id)
+{
+ struct cras_client *client = (struct cras_client *)context;
+ const char *node_name = node_name_for_node_id(client, dir, node_id);
+ printf("active node (%s): %s\n", string_for_direction(dir), node_name);
+ free((void *)node_name);
+}
+
+static void output_node_volume_changed(void *context,
+ cras_node_id_t node_id, int32_t volume)
+{
+ struct cras_client *client = (struct cras_client *)context;
+ const char *node_name =
+ node_name_for_node_id(client, CRAS_STREAM_OUTPUT, node_id);
+ printf("output node '%s' volume: %d\n", node_name, volume);
+ free((void *)node_name);
+}
+
+static void node_left_right_swapped_changed(void *context,
+ cras_node_id_t node_id, int swapped)
+{
+ struct cras_client *client = (struct cras_client *)context;
+ const char *node_name =
+ node_name_for_node_id(client, CRAS_STREAM_OUTPUT, node_id);
+ printf("output node '%s' left-right swapped: %d\n", node_name, swapped);
+ free((void *)node_name);
+}
+
+static void input_node_gain_changed(void *context,
+ cras_node_id_t node_id, int32_t gain)
+{
+ struct cras_client *client = (struct cras_client *)context;
+ const char *node_name =
+ node_name_for_node_id(client, CRAS_STREAM_INPUT, node_id);
+ printf("input node '%s' gain: %d\n", node_name, gain);
+ free((void *)node_name);
+}
+
+static void num_active_streams_changed(void *context,
+ enum CRAS_STREAM_DIRECTION dir,
+ uint32_t num_active_streams)
+{
+ printf("num active %s streams: %u\n",
+ string_for_direction(dir), num_active_streams);
+}
+
+static void server_connection_callback(struct cras_client *client,
+ cras_connection_status_t status,
+ void *user_arg)
+{
+ const char *status_str = "undefined";
+ switch (status) {
+ case CRAS_CONN_STATUS_FAILED:
+ status_str = "error";
+ break;
+ case CRAS_CONN_STATUS_DISCONNECTED:
+ status_str = "disconnected";
+ break;
+ case CRAS_CONN_STATUS_CONNECTED:
+ status_str = "connected";
+ break;
+ }
+ printf("server %s\n", status_str);
+}
+
+static void print_usage(const char *command) {
+ fprintf(stderr,
+ "%s [options]\n"
+ " Where [options] are:\n"
+ " --sync|-s - Use the synchronous connection functions.\n"
+ " --log-level|-l <n> - Set the syslog level (7 == "
+ "LOG_DEBUG).\n",
+ command);
+}
+
+int main(int argc, char **argv)
+{
+ struct cras_client *client;
+ int rc;
+ int option_character;
+ bool synchronous = false;
+ int log_level = LOG_WARNING;
+ static struct option long_options[] = {
+ {"sync", no_argument, NULL, 's'},
+ {"log-level", required_argument, NULL, 'l'},
+ {NULL, 0, NULL, 0},
+ };
+
+ while(true) {
+ int option_index = 0;
+
+ option_character = getopt_long(argc, argv, "sl:",
+ long_options, &option_index);
+ if (option_character == -1)
+ break;
+ switch (option_character) {
+ case 's':
+ synchronous = !synchronous;
+ break;
+ case 'l':
+ log_level = atoi(optarg);
+ if (log_level < 0)
+ log_level = LOG_WARNING;
+ else if (log_level > LOG_DEBUG)
+ log_level = LOG_DEBUG;
+ break;
+ default:
+ print_usage(argv[0]);
+ return 1;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr, "%s: Extra arguments.\n", argv[0]);
+ print_usage(argv[0]);
+ return 1;
+ }
+
+ openlog("cras_monitor", LOG_PERROR, LOG_USER);
+ setlogmask(LOG_UPTO(log_level));
+
+ rc = cras_client_create(&client);
+ if (rc < 0) {
+ syslog(LOG_ERR, "Couldn't create client.");
+ return rc;
+ }
+
+ cras_client_set_connection_status_cb(
+ client, server_connection_callback, NULL);
+
+ if (synchronous) {
+ rc = cras_client_connect(client);
+ if (rc != 0) {
+ syslog(LOG_ERR, "Could not connect to server.");
+ return -rc;
+ }
+ }
+
+ cras_client_set_output_volume_changed_callback(
+ client, output_volume_changed);
+ cras_client_set_output_mute_changed_callback(
+ client, output_mute_changed);
+ cras_client_set_capture_gain_changed_callback(
+ client, capture_gain_changed);
+ cras_client_set_capture_mute_changed_callback(
+ client, capture_mute_changed);
+ cras_client_set_nodes_changed_callback(
+ client, nodes_changed);
+ cras_client_set_active_node_changed_callback(
+ client, active_node_changed);
+ cras_client_set_output_node_volume_changed_callback(
+ client, output_node_volume_changed);
+ cras_client_set_node_left_right_swapped_changed_callback(
+ client, node_left_right_swapped_changed);
+ cras_client_set_input_node_gain_changed_callback(
+ client, input_node_gain_changed);
+ cras_client_set_num_active_streams_changed_callback(
+ client, num_active_streams_changed);
+ cras_client_set_state_change_callback_context(client, client);
+
+ rc = cras_client_run_thread(client);
+ if (rc != 0) {
+ syslog(LOG_ERR, "Could not start thread.");
+ return -rc;
+ }
+
+ if (!synchronous) {
+ rc = cras_client_connect_async(client);
+ if (rc) {
+ syslog(LOG_ERR, "Couldn't connect to server.\n");
+ goto destroy_exit;
+ }
+ }
+
+ while(1) {
+ int rc;
+ char c;
+ rc = read(STDIN_FILENO, &c, 1);
+ if (rc < 0 || c == 'q')
+ return 0;
+ }
+
+destroy_exit:
+ cras_client_destroy(client);
+ return 0;
+}
diff --git a/cras/src/tests/cras_router.c b/cras/src/tests/cras_router.c
new file mode 100644
index 00000000..d813bc64
--- /dev/null
+++ b/cras/src/tests/cras_router.c
@@ -0,0 +1,269 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include <fcntl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <unistd.h>
+
+#include "cras_client.h"
+#include "cras_types.h"
+#include "cras_util.h"
+#include "cras_version.h"
+
+#define PLAYBACK_BUFFERED_TIME_IN_NS (5000000)
+
+#define BUF_SIZE 32768
+
+static int keep_looping = 1;
+static int pipefd[2];
+struct cras_audio_format *aud_format;
+
+static int terminate_stream_loop(void)
+{
+ keep_looping = 0;
+ return write(pipefd[1], "1", 1);
+}
+
+static size_t get_block_size(uint64_t buffer_time_in_ns, size_t rate)
+{
+ static struct timespec t;
+
+ t.tv_nsec = buffer_time_in_ns;
+ t.tv_sec = 0;
+ return (size_t)cras_time_to_frames(&t, rate);
+}
+
+/* Run from callback thread. */
+static int got_samples(struct cras_client *client,
+ cras_stream_id_t stream_id,
+ uint8_t *captured_samples,
+ uint8_t *playback_samples,
+ unsigned int frames,
+ const struct timespec *captured_time,
+ const struct timespec *playback_time,
+ void *user_arg)
+{
+ int *fd = (int *)user_arg;
+ int ret;
+ int write_size;
+ int frame_bytes;
+
+ frame_bytes = cras_client_format_bytes_per_frame(aud_format);
+ write_size = frames * frame_bytes;
+ ret = write(*fd, captured_samples, write_size);
+ if (ret != write_size)
+ printf("Error writing file\n");
+ return frames;
+}
+
+/* Run from callback thread. */
+static int put_samples(struct cras_client *client,
+ cras_stream_id_t stream_id,
+ uint8_t *captured_samples,
+ uint8_t *playback_samples,
+ unsigned int frames,
+ const struct timespec *captured_time,
+ const struct timespec *playback_time,
+ void *user_arg)
+{
+ uint32_t frame_bytes = cras_client_format_bytes_per_frame(aud_format);
+ int fd = *(int *)user_arg;
+ uint8_t buff[BUF_SIZE];
+ int nread;
+
+ nread = read(fd, buff, MIN(frames * frame_bytes, BUF_SIZE));
+ if (nread <= 0) {
+ terminate_stream_loop();
+ return nread;
+ }
+
+ memcpy(playback_samples, buff, nread);
+ return nread / frame_bytes;
+}
+
+static int stream_error(struct cras_client *client,
+ cras_stream_id_t stream_id,
+ int err,
+ void *arg)
+{
+ printf("Stream error %d\n", err);
+ terminate_stream_loop();
+ return 0;
+}
+
+static int start_stream(struct cras_client *client,
+ cras_stream_id_t *stream_id,
+ struct cras_stream_params *params,
+ float stream_volume)
+{
+ int rc;
+
+ rc = cras_client_add_stream(client, stream_id, params);
+ if (rc < 0) {
+ fprintf(stderr, "adding a stream %d\n", rc);
+ return rc;
+ }
+ return cras_client_set_stream_volume(client,
+ *stream_id,
+ stream_volume);
+}
+
+static int run_file_io_stream(struct cras_client *client,
+ int fd,
+ int loop_fd,
+ enum CRAS_STREAM_DIRECTION direction,
+ size_t block_size,
+ size_t rate,
+ size_t num_channels)
+{
+ struct cras_stream_params *params;
+ cras_stream_id_t stream_id = 0;
+ int stream_playing = 0;
+ int *pfd = malloc(sizeof(*pfd));
+ *pfd = fd;
+ float volume_scaler = 1.0;
+
+ if (pipe(pipefd) == -1) {
+ perror("failed to open pipe");
+ return -errno;
+ }
+ aud_format = cras_audio_format_create(SND_PCM_FORMAT_S16_LE, rate,
+ num_channels);
+ if (aud_format == NULL)
+ return -ENOMEM;
+
+ params = cras_client_unified_params_create(direction,
+ block_size,
+ 0,
+ 0,
+ pfd,
+ got_samples,
+ stream_error,
+ aud_format);
+ if (params == NULL)
+ return -ENOMEM;
+
+ cras_client_run_thread(client);
+ stream_playing =
+ start_stream(client, &stream_id, params, volume_scaler) == 0;
+ if (!stream_playing)
+ return -EINVAL;
+
+ int *pfd1 = malloc(sizeof(*pfd1));
+ *pfd1 = loop_fd;
+ struct cras_stream_params *loop_params;
+ cras_stream_id_t loop_stream_id = 0;
+
+ direction = CRAS_STREAM_OUTPUT;
+
+ loop_params = cras_client_unified_params_create(direction,
+ block_size,
+ 0,
+ 0,
+ pfd1,
+ put_samples,
+ stream_error,
+ aud_format);
+ stream_playing =
+ start_stream(client, &loop_stream_id,
+ loop_params, volume_scaler) == 0;
+ if (!stream_playing)
+ return -EINVAL;
+
+ fd_set poll_set;
+
+ FD_ZERO(&poll_set);
+ FD_SET(pipefd[0], &poll_set);
+ pselect(pipefd[0] + 1, &poll_set, NULL, NULL, NULL, NULL);
+ cras_client_stop(client);
+ cras_audio_format_destroy(aud_format);
+ cras_client_stream_params_destroy(params);
+ free(pfd);
+
+ close(pipefd[0]);
+ close(pipefd[1]);
+
+ return 0;
+}
+
+static struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"rate", required_argument, 0, 'r'},
+ {0, 0, 0, 0}
+};
+
+static void show_usage(void)
+{
+ printf("--help - shows this message and exits\n");
+ printf("--rate <N> - desired sample rate\n\n");
+ printf("Running cras_router will run a loop through ");
+ printf("from the currently set input to the currently set output.\n");
+ printf("Use cras_test_client --dump_s to see all avaiable nodes and");
+ printf(" cras_test_client --set_input/output to set a node.\n");
+}
+
+int main(int argc, char **argv)
+{
+ struct cras_client *client;
+ size_t rate = 44100;
+ size_t num_channels = 2;
+ size_t block_size;
+ int rc = 0;
+ int c, option_index;
+
+ option_index = 0;
+
+ rc = cras_client_create(&client);
+ if (rc < 0) {
+ fprintf(stderr, "Couldn't create client.\n");
+ return rc;
+ }
+
+ rc = cras_client_connect(client);
+ if (rc) {
+ fprintf(stderr, "Couldn't connect to server.\n");
+ goto destroy_exit;
+ }
+
+ while (1) {
+ c = getopt_long(argc, argv, "hr:",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'h':
+ show_usage();
+ goto destroy_exit;
+ case 'r':
+ rate = atoi(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+
+ block_size = get_block_size(PLAYBACK_BUFFERED_TIME_IN_NS, rate);
+
+ /* Run loopthrough */
+ int pfd[2];
+
+ rc = pipe(pfd);
+ if (rc < 0) {
+ fprintf(stderr, "Couldn't create loopthrough pipe.\n");
+ return rc;
+ }
+ run_file_io_stream(client, pfd[1], pfd[0], CRAS_STREAM_INPUT,
+ block_size, rate, num_channels);
+
+destroy_exit:
+ cras_client_destroy(client);
+ return rc;
+}
diff --git a/cras/src/tests/cras_test_client.c b/cras/src/tests/cras_test_client.c
index 87a89f00..6db3e116 100644
--- a/cras/src/tests/cras_test_client.c
+++ b/cras/src/tests/cras_test_client.c
@@ -10,7 +10,9 @@
#include <pthread.h>
#include <stdio.h>
#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
+#include <syslog.h>
#include <sys/param.h>
#include <sys/select.h>
#include <sys/stat.h>
@@ -50,6 +52,10 @@ static int pause_in_playback_reply = 1000;
static char *channel_layout = NULL;
static int pin_device_id;
+static int play_short_sound = 0;
+static int play_short_sound_periods = 0;
+static int play_short_sound_periods_left = 0;
+
/* Conditional so the client thread can signal that main should exit. */
static pthread_mutex_t done_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t done_cond = PTHREAD_COND_INITIALIZER;
@@ -123,6 +129,9 @@ static int got_samples(struct cras_client *client,
int write_size;
int frame_bytes;
+ while (pause_client)
+ usleep(10000);
+
cras_client_calc_capture_latency(captured_time, &last_latency);
frame_bytes = cras_client_format_bytes_per_frame(aud_format);
@@ -184,6 +193,18 @@ static int put_samples(struct cras_client *client,
cras_client_calc_playback_latency(playback_time, &last_latency);
+ if (play_short_sound) {
+ if (play_short_sound_periods_left)
+ /* Play a period from file. */
+ play_short_sound_periods_left--;
+ else {
+ /* Fill zeros to play silence. */
+ memset(playback_samples, 0,
+ MIN(frames * frame_bytes, BUF_SIZE));
+ return frames;
+ }
+ }
+
nread = read(fd, buff, MIN(frames * frame_bytes, BUF_SIZE));
if (nread <= 0) {
if (exit_after_done_playing)
@@ -254,11 +275,9 @@ static void print_dev_info(const struct cras_iodev_info *devs, int num_devs)
{
unsigned i;
- printf("\tID\tName (Stable ID)\n");
- for (i = 0; i < num_devs; i++) {
- printf("\t%u\t%s (%08x)\n", devs[i].idx, devs[i].name,
- devs[i].stable_id);
- }
+ printf("\tID\tName\n");
+ for (i = 0; i < num_devs; i++)
+ printf("\t%u\t%s\n", devs[i].idx, devs[i].name);
}
static void print_node_info(const struct cras_ionode_info *nodes, int num_nodes,
@@ -266,10 +285,11 @@ static void print_node_info(const struct cras_ionode_info *nodes, int num_nodes,
{
unsigned i;
- printf("\t ID\t%4s Plugged\tL/R swapped\t "
- "Time\tType\t\t Name\n", is_input ? "Gain" : " Vol");
+ printf("\tStable Id\t ID\t%4s Plugged\tL/R swapped\t "
+ "Time Hotword\tType\t\t Name\n", is_input ? "Gain" : " Vol");
for (i = 0; i < num_nodes; i++)
- printf("\t%u:%u\t%5g %7s\t%14s\t%10ld\t%-16s%c%s\n",
+ printf("\t(%08x)\t%u:%u\t%5g %7s\t%14s\t%10ld %-7s\t%-16s%c%s\n",
+ nodes[i].stable_id,
nodes[i].iodev_idx,
nodes[i].ionode_idx,
is_input ? nodes[i].capture_gain / 100.0
@@ -277,6 +297,7 @@ static void print_node_info(const struct cras_ionode_info *nodes, int num_nodes,
nodes[i].plugged ? "yes" : "no",
nodes[i].left_right_swapped ? "yes" : "no",
(long) nodes[i].plugged_time.tv_sec,
+ nodes[i].active_hotword_model,
nodes[i].type,
nodes[i].active ? '*' : ' ',
nodes[i].name);
@@ -363,7 +384,7 @@ static void print_user_muted(struct cras_client *client)
static void show_alog_tag(const struct audio_thread_event_log *log,
unsigned int tag_idx)
{
- unsigned int tag = (log->log[tag_idx].tag_sec >> 24) & 0x1f;
+ 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;
@@ -374,127 +395,152 @@ static void show_alog_tag(const struct audio_thread_event_log *log,
if (log->log[tag_idx].tag_sec == 0 && log->log[tag_idx].nsec == 0)
return;
+ printf("%10u.%09u ", sec, nsec);
+
switch (tag) {
case AUDIO_THREAD_WAKE:
- printf("WAKE: %u.%09u num_fds %d\n", sec, nsec, (int)data1);
+ printf("%-30s num_fds:%d\n", "WAKE", (int)data1);
break;
case AUDIO_THREAD_SLEEP:
- printf("SLEEP: %u.%09u %09d.%09d long:%09d\n", sec, nsec,
- (int)data1, (int)data2, (int)data3);
+ printf("%-30s sleep:%09d.%09d longest_wake:%09d\n",
+ "SLEEP", (int)data1, (int)data2, (int)data3);
break;
case AUDIO_THREAD_READ_AUDIO:
- printf("READ_AUDIO: %u.%09u dev: %x hw_level: %u read %u\n",
- sec, nsec, data1, data2, data3);
+ printf("%-30s dev:%u hw_level:%u read:%u\n",
+ "READ_AUDIO", data1, data2, data3);
+ break;
+ case AUDIO_THREAD_READ_AUDIO_TSTAMP:
+ printf("%-30s dev:%u tstamp:%09d.%09d\n",
+ "READ_AUDIO_TSTAMP", data1, (int)data2, (int)data3);
break;
case AUDIO_THREAD_READ_AUDIO_DONE:
- printf("READ_AUDIO_DONE: %u.%09u read remainder %u\n",
- sec, nsec, data1);
+ printf("%-30s read_remainder:%u\n", "READ_AUDIO_DONE", data1);
+ break;
+ case AUDIO_THREAD_READ_OVERRUN:
+ printf("%-30s dev:%u stream:%x num_overruns:%u\n",
+ "READ_AUDIO_OVERRUN", data1, data2, data3);
break;
case AUDIO_THREAD_FILL_AUDIO:
- printf("FILL_AUDIO: %u.%09u dev %x hw_level %u\n",
- sec, nsec, data1, data2);
+ printf("%-30s dev:%u hw_level:%u\n",
+ "FILL_AUDIO", data1, data2);
+ break;
+ case AUDIO_THREAD_FILL_AUDIO_TSTAMP:
+ printf("%-30s dev:%u tstamp:%09d.%09d\n",
+ "FILL_AUDIO_TSTAMP", data1, (int)data2, (int)data3);
break;
case AUDIO_THREAD_FILL_AUDIO_DONE:
- printf("FILL_AUDIO_DONE: %u.%09u total_written %u\n",
- sec, nsec, data1);
+ printf("%-30s hw_level:%u total_written:%u min_cb_level:%u\n",
+ "FILL_AUDIO_DONE", data1, data2, data3);
break;
case AUDIO_THREAD_WRITE_STREAMS_WAIT:
- printf("WRITE_STREAMS_WAIT: %u.%09u for %u.%06u\n",
- sec, nsec, data1, data2);
+ printf("%-30s stream:%x\n", "WRITE_STREAMS_WAIT", data1);
break;
case AUDIO_THREAD_WRITE_STREAMS_WAIT_TO:
- printf("WRITE_STREAMS_WAIT_TO: %u.%09u\n",
- sec, nsec);
+ printf("%-30s\n", "WRITE_STREAMS_WAIT_TO");
break;
case AUDIO_THREAD_WRITE_STREAMS_MIX:
- printf("WRITE_STREAMS_MIX: %u.%09u wlimit %u max_offset %u\n",
- sec, nsec, data1, data2);
+ printf("%-30s write_limit:%u max_offset:%u\n",
+ "WRITE_STREAMS_MIX", data1, data2);
break;
case AUDIO_THREAD_WRITE_STREAMS_MIXED:
- printf("WRITE_STREAMS_MIXED: %u.%09u write_limit %u\n",
- sec, nsec, data1);
+ printf("%-30s write_limit:%u\n", "WRITE_STREAMS_MIXED", data1);
break;
case AUDIO_THREAD_WRITE_STREAMS_STREAM:
- printf("WRITE_STREAMS_STREAM: %u.%09u id %x "
- "shm_frames %u cb_pending %u\n",
- sec, nsec, data1, data2, data3);
+ printf("%-30s id:%x shm_frames:%u cb_pending:%u\n",
+ "WRITE_STREAMS_STREAM", data1, data2, data3);
break;
case AUDIO_THREAD_FETCH_STREAM:
- printf("WRITE_STREAMS_FETCH_STREAM: %u.%09u id %x cbth %u delay %u\n",
- sec, nsec, data1, data2, data3);
+ printf("%-30s id:%x cbth:%u delay:%u\n",
+ "WRITE_STREAMS_FETCH_STREAM", data1, data2, data3);
break;
case AUDIO_THREAD_STREAM_ADDED:
- printf("STREAM_ADDED: %u.%9u id %x dev_idx %u\n",
- sec, nsec, data1, data2);
+ printf("%-30s id:%x dev:%u\n",
+ "STREAM_ADDED", data1, data2);
break;
case AUDIO_THREAD_STREAM_REMOVED:
- printf("STREAM_REMOVED: %u.%9u id %x\n", sec, nsec, data1);
+ printf("%-30s id:%x\n", "STREAM_REMOVED", data1);
break;
case AUDIO_THREAD_A2DP_ENCODE:
- printf("A2DP_ENCODE: %u.%09u proc %d queued %u readable %u\n",
- sec, nsec, data1, data2, data3);
+ printf("%-30s proc:%d queued:%u readable:%u\n",
+ "A2DP_ENCODE", data1, data2, data3);
break;
case AUDIO_THREAD_A2DP_WRITE:
- printf("A2DP_WRITE: %u.%09u written %d queued %u\n",
- sec, nsec, data1, data2);
+ printf("%-30s written:%d queued:%u\n",
+ "A2DP_WRITE", data1, data2);
break;
case AUDIO_THREAD_DEV_STREAM_MIX:
- printf("DEV_STREAM_MIX: %u.%09u written %u read %u\n",
- sec, nsec, data1, data2);
+ printf("%-30s written:%u read:%u\n",
+ "DEV_STREAM_MIX", data1, data2);
break;
case AUDIO_THREAD_CAPTURE_POST:
- printf("CAPTURE_POST: %u.%09u stream %x thresh %u rd_buf %u\n",
- sec, nsec, data1, data2, data3);
+ printf("%-30s stream:%x thresh:%u rd_buf:%u\n",
+ "CAPTURE_POST", data1, data2, data3);
break;
case AUDIO_THREAD_CAPTURE_WRITE:
- printf("CAPTURE_WRITE: %u.%09u stream %x write %u shm_fr %u\n",
- sec, nsec, data1, data2, data3);
+ printf("%-30s stream:%x write:%u shm_fr:%u\n",
+ "CAPTURE_WRITE", data1, data2, data3);
break;
case AUDIO_THREAD_CONV_COPY:
- printf("CONV_COPY: %u.%09u wr_buf %u shm_writable %u"
- "offset %u\n",
- sec, nsec, data1, data2, data3);
+ printf("%-30s wr_buf:%u shm_writable:%u offset:%u\n",
+ "CONV_COPY", data1, data2, data3);
break;
case AUDIO_THREAD_STREAM_SLEEP_TIME:
- printf("STREAM_SLEEP_TIME: %u.%09u id:%x wake:%09u.%09d\n",
- sec, nsec, data1, data2, data3);
+ printf("%-30s id:%x wake:%09u.%09d\n",
+ "STREAM_SLEEP_TIME", data1, (int)data2, (int)data3);
break;
case AUDIO_THREAD_STREAM_SLEEP_ADJUST:
- printf("STREAM_SLEEP_ADJUST: %u.%09u id:%x from:%09u.%09d\n",
- sec, nsec, data1, data2, data3);
+ printf("%-30s id:%x from:%09u.%09d\n",
+ "STREAM_SLEEP_ADJUST", data1, data2, data3);
break;
case AUDIO_THREAD_STREAM_SKIP_CB:
- printf("STREAM_SKIP_CB: %u.%9u id %x write offsets %u %u\n",
- sec, nsec, data1, data2, data3);
+ printf("%-30s id:%x write_offset_0:%u write_offset_1:%u\n",
+ "STREAM_SKIP_CB", data1, data2, data3);
break;
case AUDIO_THREAD_DEV_SLEEP_TIME:
- printf("DEV_SLEEP_TIME: %u.%09u devidx:%x wake:%09u.%09d\n",
- sec, nsec, data1, data2, data3);
+ printf("%-30s dev:%u wake:%09u.%09d\n",
+ "DEV_SLEEP_TIME", data1, data2, data3);
break;
case AUDIO_THREAD_SET_DEV_WAKE:
- printf("SET_DEV_WAKE: %u.%09u devidx:%x hw_level:%u "
- "sleep:%u\n", sec, nsec, data1, data2, data3);
+ printf("%-30s dev:%u hw_level:%u sleep:%u\n",
+ "SET_DEV_WAKE", data1, data2, data3);
break;
case AUDIO_THREAD_DEV_ADDED:
- printf("DEV_ADDED: %u.%09u devidx:%x\n",
- sec, nsec, data1);
+ printf("%-30s dev:%u\n", "DEV_ADDED", data1);
break;
case AUDIO_THREAD_DEV_REMOVED:
- printf("DEV_REMOVED: %u.%09u devidx:%x\n",
- sec, nsec, data1);
+ printf("%-30s dev:%u\n", "DEV_REMOVED", data1);
break;
case AUDIO_THREAD_IODEV_CB:
- printf("IODEV_CB: %u.%09u is_write:%u\n", sec, nsec, data1);
+ printf("%-30s is_write:%u\n", "IODEV_CB", data1);
break;
case AUDIO_THREAD_PB_MSG:
- printf("PB_MSG: %u.%09u msg_id:%u\n", sec, nsec, data1);
+ printf("%-30s msg_id:%u\n", "PB_MSG", data1);
break;
case AUDIO_THREAD_ODEV_NO_STREAMS:
- printf("ODEV_NO_STREAMS: %u.%09u id:%u hw_level:%u cb_lev:%u\n",
- sec, nsec, data1, data2, data3);
+ printf("%-30s dev:%u\n",
+ "ODEV_NO_STREAMS", data1);
+ break;
+ case AUDIO_THREAD_ODEV_LEAVE_NO_STREAMS:
+ printf("%-30s dev:%u\n",
+ "ODEV_LEAVE_NO_STREAMS", data1);
+ break;
+ case AUDIO_THREAD_ODEV_START:
+ printf("%-30s dev:%u min_cb_level:%u\n",
+ "ODEV_START", data1, data2);
+ break;
+ case AUDIO_THREAD_FILL_ODEV_ZEROS:
+ printf("%-30s dev:%u write:%u\n",
+ "FILL_ODEV_ZEROS", data1, data2);
+ break;
+ case AUDIO_THREAD_ODEV_DEFAULT_NO_STREAMS:
+ printf("%-30s dev:%u hw_level:%u target:%u\n",
+ "DEFAULT_NO_STREAMS", data1, data2, data3);
+ break;
+ case AUDIO_THREAD_SEVERE_UNDERRUN:
+ printf("%-30s dev:%u\n", "SEVERE_UNDERRUN", data1);
break;
default:
- printf("Unknown alog tag %u\n", tag);
+ printf("%-30s tag:%u\n","UNKNOWN", tag);
break;
}
}
@@ -518,14 +564,25 @@ static void audio_debug_info(struct cras_client *client)
(info->devs[i].direction == CRAS_STREAM_INPUT)
? "Input" : "Output",
info->devs[i].dev_name);
- printf("%u %u %u %u %u %u %lf\n",
+ printf("buffer_size: %u\n"
+ "min_buffer_level: %u\n"
+ "min_cb_level: %u\n"
+ "max_cb_level: %u\n"
+ "frame_rate: %u\n"
+ "num_channels: %u\n"
+ "est_rate_ratio: %lf\n"
+ "num_underruns: %u\n"
+ "num_severe_underruns: %u\n",
(unsigned int)info->devs[i].buffer_size,
(unsigned int)info->devs[i].min_buffer_level,
(unsigned int)info->devs[i].min_cb_level,
(unsigned int)info->devs[i].max_cb_level,
(unsigned int)info->devs[i].frame_rate,
(unsigned int)info->devs[i].num_channels,
- info->devs[i].est_rate_ratio);
+ info->devs[i].est_rate_ratio,
+ (unsigned int)info->devs[i].num_underruns,
+ (unsigned int)info->devs[i].num_severe_underruns);
+ printf("\n");
}
printf("-------------stream_dump------------\n");
@@ -534,20 +591,31 @@ static void audio_debug_info(struct cras_client *client)
for (i = 0; i < info->num_streams; i++) {
int channel;
- printf("stream: %llx dev: %x\n",
+ printf("stream: %llx dev: %u\n",
(unsigned long long)info->streams[i].stream_id,
(unsigned int)info->streams[i].dev_idx);
- printf("%d %u %u %u %u %u.%09u\n",
- info->streams[i].direction,
+ printf("direction: %s\n",
+ (info->streams[i].direction == CRAS_STREAM_INPUT)
+ ? "Input" : "Output");
+ printf("stream_type: %s\n",
+ cras_stream_type_str(info->streams[i].stream_type));
+ printf("buffer_frames: %u\n"
+ "cb_threshold: %u\n"
+ "frame_rate: %u\n"
+ "num_channels: %u\n"
+ "longest_fetch_sec: %u.%09u\n"
+ "num_overruns: %u\n",
(unsigned int)info->streams[i].buffer_frames,
(unsigned int)info->streams[i].cb_threshold,
(unsigned int)info->streams[i].frame_rate,
(unsigned int)info->streams[i].num_channels,
(unsigned int)info->streams[i].longest_fetch_sec,
- (unsigned int)info->streams[i].longest_fetch_nsec);
+ (unsigned int)info->streams[i].longest_fetch_nsec,
+ (unsigned int)info->streams[i].num_overruns);
+ printf("channel map:");
for (channel = 0; channel < CRAS_CH_MAX; channel++)
printf("%d ", info->streams[i].channel_layout[channel]);
- printf("\n");
+ printf("\n\n");
}
printf("Audio Thread Event Log:\n");
@@ -605,6 +673,7 @@ static int run_file_io_stream(struct cras_client *client,
int fd,
enum CRAS_STREAM_DIRECTION direction,
size_t block_size,
+ enum CRAS_STREAM_TYPE stream_type,
size_t rate,
size_t num_channels,
uint32_t flags,
@@ -668,7 +737,7 @@ static int run_file_io_stream(struct cras_client *client,
params = cras_client_unified_params_create(direction,
block_size,
- 0,
+ stream_type,
flags,
pfd,
aud_cb,
@@ -803,6 +872,9 @@ static int run_file_io_stream(struct cras_client *client,
cras_client_get_system_min_capture_gain(client),
cras_client_get_system_max_capture_gain(client));
break;
+ case '\'':
+ play_short_sound_periods_left = play_short_sound_periods;
+ break;
case '\n':
break;
default:
@@ -829,6 +901,7 @@ static int run_file_io_stream(struct cras_client *client,
static int run_capture(struct cras_client *client,
const char *file,
size_t block_size,
+ enum CRAS_STREAM_TYPE stream_type,
size_t rate,
size_t num_channels,
int is_loopback)
@@ -839,8 +912,8 @@ static int run_capture(struct cras_client *client,
return -errno;
}
- run_file_io_stream(client, fd, CRAS_STREAM_INPUT, block_size, rate,
- num_channels, 0, is_loopback);
+ run_file_io_stream(client, fd, CRAS_STREAM_INPUT, block_size,
+ stream_type, rate, num_channels, 0, is_loopback);
close(fd);
return 0;
@@ -849,6 +922,7 @@ static int run_capture(struct cras_client *client,
static int run_playback(struct cras_client *client,
const char *file,
size_t block_size,
+ enum CRAS_STREAM_TYPE stream_type,
size_t rate,
size_t num_channels)
{
@@ -860,8 +934,8 @@ static int run_playback(struct cras_client *client,
return -errno;
}
- run_file_io_stream(client, fd, CRAS_STREAM_OUTPUT,
- block_size, rate, num_channels, 0, 0);
+ run_file_io_stream(client, fd, CRAS_STREAM_OUTPUT, block_size,
+ stream_type, rate, num_channels, 0, 0);
close(fd);
return 0;
@@ -871,8 +945,9 @@ static int run_hotword(struct cras_client *client,
size_t block_size,
size_t rate)
{
- run_file_io_stream(client, -1, CRAS_STREAM_INPUT, block_size, rate, 1,
- HOTWORD_STREAM, 0);
+ run_file_io_stream(client, -1, CRAS_STREAM_INPUT, block_size,
+ CRAS_STREAM_TYPE_DEFAULT, rate, 1, HOTWORD_STREAM,
+ 0);
return 0;
}
static void print_server_info(struct cras_client *client)
@@ -902,6 +977,30 @@ static void print_audio_debug_info(struct cras_client *client)
pthread_mutex_unlock(&done_mutex);
}
+static void hotword_models_cb(struct cras_client *client,
+ const char *hotword_models)
+{
+ printf("Hotword models: %s\n", hotword_models);
+}
+
+static void print_hotword_models(struct cras_client *client,
+ cras_node_id_t id)
+{
+ struct timespec wait_time;
+
+ cras_client_run_thread(client);
+ cras_client_connected_wait(client);
+ cras_client_get_hotword_models(client, id,
+ hotword_models_cb);
+
+ 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 check_output_plugged(struct cras_client *client, const char *name)
{
cras_client_run_thread(client);
@@ -910,6 +1009,24 @@ static void check_output_plugged(struct cras_client *client, const char *name)
cras_client_output_dev_plugged(client, name) ? "Yes" : "No");
}
+/* Repeatedly mute and unmute the output until there is an error. */
+static void mute_loop_test(struct cras_client *client, int auto_reconnect)
+{
+ int mute = 0;
+ int rc;
+
+ if (auto_reconnect)
+ cras_client_run_thread(client);
+ while(1) {
+ rc = cras_client_set_user_mute(client, mute);
+ printf("cras_client_set_user_mute(%d): %d\n", mute, rc);
+ if (rc != 0 && !auto_reconnect)
+ return;
+ mute = !mute;
+ sleep(2);
+ }
+}
+
static struct option long_options[] = {
{"show_latency", no_argument, &show_latency, 1},
{"show_rms", no_argument, &show_rms, 1},
@@ -950,6 +1067,13 @@ static struct option long_options[] = {
{"pin_device", required_argument, 0, '8'},
{"suspend", required_argument, 0, '9'},
{"set_node_gain", required_argument, 0, ':'},
+ {"play_short_sound", required_argument, 0, '!'},
+ {"config_global_remix", required_argument, 0, ';'},
+ {"set_hotword_model", required_argument, 0, '<'},
+ {"get_hotword_models", required_argument, 0, '>'},
+ {"syslog_mask", required_argument, 0, 'L'},
+ {"mute_loop_test", required_argument, 0, 'M'},
+ {"stream_type", required_argument, 0, 'T'},
{0, 0, 0, 0}
};
@@ -970,15 +1094,19 @@ static void show_usage()
printf("--dump_dsp - Print status of dsp to syslog.\n");
printf("--dump_server_info - Print status of the server.\n");
printf("--duration_seconds <N> - Seconds to record or playback.\n");
+ printf("--get_hotword_models <N>:<M> - Get the supported hotword models of node\n");
printf("--help - Print this message.\n");
printf("--listen_for_hotword - Listen for a hotword if supported\n");
printf("--loopback_file <name> - Name of file to record loopback to.\n");
printf("--mute <0|1> - Set system mute state.\n");
+ printf("--mute_loop_test <0|1> - Continuously loop mute/umute. Argument: 0 - stop on error.\n"
+ " 1 - automatically reconnect to CRAS.\n");
printf("--num_channels <N> - Two for stereo.\n");
printf("--pin_device <N> - Playback/Capture only on the given device."
"\n");
printf("--playback_file <name> - Name of file to play, "
"\"-\" to playback raw audio from stdin.\n");
+ printf("--play_short_sound <N> - Plays the content in the file for N periods when ' is pressed.\n");
printf("--plug <N>:<M>:<0|1> - Set the plug state (0 or 1) for the"
" ionode with the given index M on the device with index N\n");
printf("--rate <N> - Specifies the sample rate in Hz.\n");
@@ -989,6 +1117,7 @@ static void show_usage()
"id from active output device list\n");
printf("--select_input <N>:<M> - Select the ionode with the given id as preferred input\n");
printf("--select_output <N>:<M> - Select the ionode with the given id as preferred output\n");
+ printf("--set_hotword_model <N>:<M>:<model> - Set the model to node\n");
printf("--playback_delay_us <N> - Set the time in us to delay a reply for playback when i is pressed\n");
printf("--set_node_volume <N>:<M>:<0-100> - Set the volume of the ionode with the given id\n");
printf("--show_latency - Display latency while playing or recording.\n");
@@ -998,6 +1127,8 @@ static void show_usage()
printf("--swap_left_right <N>:<M>:<0|1> - Swap or unswap (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");
+ printf("--syslog_mask <n> - Set the syslog mask to the given log level.\n");
printf("--test_hotword_file <N>:<filename> - Use filename as a hotword buffer for device N\n");
printf("--user_mute <0|1> - Set user mute state.\n");
printf("--version - Print the git commit ID that was used to build the client.\n");
@@ -1015,9 +1146,12 @@ int main(int argc, char **argv)
const char *capture_file = NULL;
const char *playback_file = NULL;
const char *loopback_file = NULL;
+ enum CRAS_STREAM_TYPE stream_type = CRAS_STREAM_TYPE_DEFAULT;
int rc = 0;
option_index = 0;
+ openlog("cras_test_client", LOG_PERROR, LOG_USER);
+ setlogmask(LOG_UPTO(LOG_INFO));
rc = cras_client_create(&client);
if (rc < 0) {
@@ -1025,7 +1159,7 @@ int main(int argc, char **argv)
return rc;
}
- rc = cras_client_connect(client);
+ rc = cras_client_connect_timeout(client, 1000);
if (rc) {
fprintf(stderr, "Couldn't connect to server.\n");
goto destroy_exit;
@@ -1121,9 +1255,6 @@ int main(int argc, char **argv)
break;
}
case 'y':
- case 'z':
- pause_in_playback_reply = atoi(optarg);
- break;
case 'a': {
int dev_index = atoi(strtok(optarg, ":"));
int node_index = atoi(strtok(NULL, ":"));
@@ -1135,6 +1266,9 @@ int main(int argc, char **argv)
cras_client_select_node(client, direction, id);
break;
}
+ case 'z':
+ pause_in_playback_reply = atoi(optarg);
+ break;
case 'k':
case 't':
case '1':
@@ -1246,6 +1380,77 @@ int main(int argc, char **argv)
cras_client_set_suspend(client, suspend);
break;
}
+ case '!': {
+ play_short_sound = 1;
+ play_short_sound_periods = atoi(optarg);
+ break;
+ }
+ case ';': {
+ char *s;
+ int nch;
+ int size = 0;
+ float *coeff;
+
+ s = strtok(optarg, ":");
+ nch = atoi(s);
+ coeff = (float *)calloc(nch * nch,
+ sizeof(*coeff));
+ for (size = 0; size < nch * nch; size++) {
+ s = strtok(NULL, ",");
+ if (NULL == s)
+ break;
+ coeff[size] = atof(s);
+ }
+ cras_client_config_global_remix(client, nch, coeff);
+ free(coeff);
+ break;
+ }
+ case '<':
+ case '>': {
+ char *s;
+ int dev_index;
+ int node_index;
+
+ s = strtok(optarg, ":");
+ if (!s) {
+ show_usage();
+ return -EINVAL;
+ }
+ dev_index = atoi(s);
+
+ s = strtok(NULL, ":");
+ if (!s) {
+ show_usage();
+ return -EINVAL;
+ }
+ node_index = atoi(s);
+
+ s = strtok(NULL, ":");
+ if (!s && c == ';') {
+ show_usage();
+ return -EINVAL;
+ }
+
+ cras_node_id_t id = cras_make_node_id(dev_index,
+ node_index);
+ if (c == '<')
+ cras_client_set_hotword_model(client, id, s);
+ else
+ print_hotword_models(client, id);
+ break;
+ }
+ case 'L': {
+ int log_level = atoi(optarg);
+
+ setlogmask(LOG_UPTO(log_level));
+ break;
+ }
+ case 'M':
+ mute_loop_test(client, atoi(optarg));
+ break;
+ case 'T':
+ stream_type = atoi(optarg);
+ break;
default:
break;
}
@@ -1258,20 +1463,22 @@ int main(int argc, char **argv)
if (capture_file != NULL) {
if (strcmp(capture_file, "-") == 0)
rc = run_file_io_stream(client, 1, CRAS_STREAM_INPUT,
- block_size, rate, num_channels, 0, 0);
+ block_size, stream_type, rate,
+ num_channels, 0, 0);
else
- rc = run_capture(client, capture_file,
- block_size, rate, num_channels, 0);
+ rc = run_capture(client, capture_file, block_size,
+ stream_type, rate, num_channels, 0);
} else if (playback_file != NULL) {
if (strcmp(playback_file, "-") == 0)
rc = run_file_io_stream(client, 0, CRAS_STREAM_OUTPUT,
- block_size, rate, num_channels, 0, 0);
+ block_size, stream_type, rate,
+ num_channels, 0, 0);
else
- rc = run_playback(client, playback_file,
- block_size, rate, num_channels);
+ rc = run_playback(client, playback_file, block_size,
+ stream_type, rate, num_channels);
} else if (loopback_file != NULL) {
- rc = run_capture(client, loopback_file,
- block_size, rate, num_channels, 1);
+ rc = run_capture(client, loopback_file, block_size,
+ stream_type, rate, num_channels, 1);
}
destroy_exit:
diff --git a/cras/src/tests/dev_stream_unittest.cc b/cras/src/tests/dev_stream_unittest.cc
index 19ff40d7..8f90ed6c 100644
--- a/cras/src/tests/dev_stream_unittest.cc
+++ b/cras/src/tests/dev_stream_unittest.cc
@@ -22,6 +22,7 @@ struct audio_thread_event_log *atlog;
};
static struct timespec clock_gettime_retspec;
+static struct timespec cb_ts;
static const int kBufferFrames = 1024;
static const struct cras_audio_format fmt_s16le_44_1 = {
@@ -46,7 +47,7 @@ struct cras_audio_area_copy_call {
unsigned int dst_format_bytes;
const struct cras_audio_area *src;
unsigned int src_offset;
- unsigned int src_index;
+ float software_gain_scaler;
};
struct fmt_conv_call {
@@ -92,6 +93,9 @@ static struct rstream_get_readable_call rstream_get_readable_call;
static unsigned int rstream_get_readable_num;
static uint8_t *rstream_get_readable_ptr;
+static int cras_rstream_audio_ready_called;
+static int cras_rstream_audio_ready_count;
+
class CreateSuite : public testing::Test{
protected:
virtual void SetUp() {
@@ -117,6 +121,9 @@ class CreateSuite : public testing::Test{
cras_fmt_conversion_needed_val = 0;
cras_fmt_conv_set_linear_resample_rates_called = 0;
+ cras_rstream_audio_ready_called = 0;
+ cras_rstream_audio_ready_count = 0;
+
memset(&copy_area_call, 0xff, sizeof(copy_area_call));
memset(&conv_frames_call, 0xff, sizeof(conv_frames_call));
@@ -153,12 +160,12 @@ TEST_F(CreateSuite, CaptureNoSRC) {
struct cras_audio_area *area;
struct cras_audio_area *stream_area;
int16_t cap_buf[kBufferFrames * 2];
+ float software_gain_scaler = 10;
devstr.stream = &rstream_;
devstr.conv = NULL;
devstr.conv_buffer = NULL;
devstr.conv_buffer_size_frames = 0;
- devstr.skip_mix = 0;
area = (struct cras_audio_area*)calloc(1, sizeof(*area) +
2 * sizeof(*area->channels));
@@ -180,13 +187,13 @@ TEST_F(CreateSuite, CaptureNoSRC) {
stream_area->channels[1].step_bytes = 4;
stream_area->channels[1].buf = (uint8_t *)(shm_samples + 1);
- dev_stream_capture(&devstr, area, 0, 0);
+ dev_stream_capture(&devstr, area, 0, software_gain_scaler);
EXPECT_EQ(stream_area, copy_area_call.dst);
EXPECT_EQ(0, copy_area_call.dst_offset);
EXPECT_EQ(4, copy_area_call.dst_format_bytes);
EXPECT_EQ(area, copy_area_call.src);
- EXPECT_EQ(1, copy_area_call.src_index);
+ EXPECT_EQ(software_gain_scaler, copy_area_call.software_gain_scaler);
free(area);
free(stream_area);
@@ -197,13 +204,13 @@ TEST_F(CreateSuite, CaptureSRC) {
struct cras_audio_area *area;
struct cras_audio_area *stream_area;
int16_t cap_buf[kBufferFrames * 2];
+ float software_gain_scaler = 10;
devstr.stream = &rstream_;
devstr.conv = (struct cras_fmt_conv *)0xdead;
devstr.conv_buffer =
(struct byte_buffer *)byte_buffer_create(kBufferFrames * 2 * 4);
devstr.conv_buffer_size_frames = kBufferFrames * 2;
- devstr.skip_mix = 0;
area = (struct cras_audio_area*)calloc(1, sizeof(*area) +
2 * sizeof(*area->channels));
@@ -239,7 +246,7 @@ TEST_F(CreateSuite, CaptureSRC) {
conv_frames_ret = kBufferFrames / 2;
cras_fmt_conv_convert_frames_in_frames_val = kBufferFrames;
cras_fmt_conversion_needed_val = 1;
- dev_stream_capture(&devstr, area, 0, 0);
+ dev_stream_capture(&devstr, area, 0, software_gain_scaler);
EXPECT_EQ((struct cras_fmt_conv *)0xdead, conv_frames_call.conv);
EXPECT_EQ((uint8_t *)cap_buf, conv_frames_call.in_buf);
@@ -251,41 +258,14 @@ TEST_F(CreateSuite, CaptureSRC) {
EXPECT_EQ(0, copy_area_call.dst_offset);
EXPECT_EQ(4, copy_area_call.dst_format_bytes);
EXPECT_EQ(devstr.conv_area, copy_area_call.src);
- EXPECT_EQ(1, copy_area_call.src_index);
EXPECT_EQ(conv_frames_ret, devstr.conv_area->frames);
+ EXPECT_EQ(software_gain_scaler, copy_area_call.software_gain_scaler);
free(area);
free(stream_area);
free(devstr.conv_area);
}
-TEST_F(CreateSuite, CreateNoSRCOutput) {
- struct dev_stream *dev_stream;
-
- rstream_.format = fmt_s16le_44_1;
- in_fmt.frame_rate = 44100;
- out_fmt.frame_rate = 44100;
- dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void *)0x55);
- EXPECT_EQ(1, config_format_converter_called);
- EXPECT_EQ(NULL, dev_stream->conv_buffer);
- EXPECT_EQ(0, dev_stream->conv_buffer_size_frames);
- dev_stream_destroy(dev_stream);
-}
-
-TEST_F(CreateSuite, CreateNoSRCInput) {
- struct dev_stream *dev_stream;
-
- rstream_.format = fmt_s16le_44_1;
- rstream_.direction = CRAS_STREAM_INPUT;
- in_fmt.frame_rate = 44100;
- out_fmt.frame_rate = 44100;
- dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void *)0x55);
- EXPECT_EQ(1, config_format_converter_called);
- EXPECT_EQ(NULL, dev_stream->conv_buffer);
- EXPECT_EQ(0, dev_stream->conv_buffer_size_frames);
- dev_stream_destroy(dev_stream);
-}
-
TEST_F(CreateSuite, CreateSRC44to48) {
struct dev_stream *dev_stream;
@@ -293,7 +273,8 @@ TEST_F(CreateSuite, CreateSRC44to48) {
in_fmt.frame_rate = 44100;
out_fmt.frame_rate = 48000;
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
- dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_48, (void *)0x55);
+ dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_48, (void *)0x55,
+ &cb_ts);
EXPECT_EQ(1, config_format_converter_called);
EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
EXPECT_LE(cras_frames_at_rate(in_fmt.frame_rate, kBufferFrames,
@@ -310,7 +291,8 @@ TEST_F(CreateSuite, CreateSRC44to48Input) {
in_fmt.frame_rate = 48000;
out_fmt.frame_rate = 44100;
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
- dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_48, (void *)0x55);
+ dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_48, (void *)0x55,
+ &cb_ts);
EXPECT_EQ(1, config_format_converter_called);
EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
EXPECT_LE(cras_frames_at_rate(in_fmt.frame_rate, kBufferFrames,
@@ -326,7 +308,8 @@ TEST_F(CreateSuite, CreateSRC48to44) {
in_fmt.frame_rate = 48000;
out_fmt.frame_rate = 44100;
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
- dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void *)0x55);
+ dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void *)0x55,
+ &cb_ts);
EXPECT_EQ(1, config_format_converter_called);
EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
EXPECT_LE(cras_frames_at_rate(in_fmt.frame_rate, kBufferFrames,
@@ -343,7 +326,8 @@ TEST_F(CreateSuite, CreateSRC48to44Input) {
in_fmt.frame_rate = 44100;
out_fmt.frame_rate = 48000;
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
- dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void *)0x55);
+ dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void *)0x55,
+ &cb_ts);
EXPECT_EQ(1, config_format_converter_called);
EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
EXPECT_LE(cras_frames_at_rate(in_fmt.frame_rate, kBufferFrames,
@@ -360,7 +344,8 @@ TEST_F(CreateSuite, CreateSRC48to44StereoToMono) {
in_fmt.frame_rate = 44100;
out_fmt.frame_rate = 48000;
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
- dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void *)0x55);
+ dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void *)0x55,
+ &cb_ts);
EXPECT_EQ(1, config_format_converter_called);
EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
EXPECT_LE(cras_frames_at_rate(in_fmt.frame_rate, kBufferFrames,
@@ -379,7 +364,8 @@ TEST_F(CreateSuite, CaptureAvailConvBufHasSamples) {
rstream_.format = fmt_s16le_48;
rstream_.direction = CRAS_STREAM_INPUT;
config_format_converter_conv = reinterpret_cast<struct cras_fmt_conv*>(0x33);
- dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void *)0x55);
+ dev_stream = dev_stream_create(&rstream_, 0, &fmt_s16le_44_1, (void *)0x55,
+ &cb_ts);
EXPECT_EQ(1, config_format_converter_called);
EXPECT_NE(static_cast<byte_buffer*>(NULL), dev_stream->conv_buffer);
EXPECT_LE(cras_frames_at_rate(in_fmt.frame_rate, kBufferFrames,
@@ -407,7 +393,7 @@ TEST_F(CreateSuite, SetDevRateNotMasterDev) {
config_format_converter_conv =
reinterpret_cast<struct cras_fmt_conv*>(0x33);
dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
- (void *)0x55);
+ (void *)0x55, &cb_ts);
dev_stream_set_dev_rate(dev_stream, 44100, 1.01, 1.0, 0);
EXPECT_EQ(1, cras_fmt_conv_set_linear_resample_rates_called);
@@ -437,7 +423,7 @@ TEST_F(CreateSuite, SetDevRateMasterDev) {
config_format_converter_conv =
reinterpret_cast<struct cras_fmt_conv*>(0x33);
dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
- (void *)0x55);
+ (void *)0x55, &cb_ts);
dev_stream_set_dev_rate(dev_stream, 44100, 1.01, 1.0, 0);
EXPECT_EQ(1, cras_fmt_conv_set_linear_resample_rates_called);
@@ -531,7 +517,7 @@ TEST_F(CreateSuite, StreamCanFetch) {
unsigned int dev_id = 9;
dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
- (void *)0x55);
+ (void *)0x55, &cb_ts);
/* Verify stream cannot fetch when it's still pending. */
cras_shm_set_callback_pending(&rstream_.shm, 1);
@@ -550,6 +536,277 @@ TEST_F(CreateSuite, StreamCanFetch) {
dev_stream_destroy(dev_stream);
}
+TEST_F(CreateSuite, StreamCanSend) {
+ struct dev_stream *dev_stream;
+ unsigned int dev_id = 9;
+ int written_frames;
+ int rc;
+ struct timespec expected_next_cb_ts;
+
+ rstream_.direction = CRAS_STREAM_INPUT;
+ dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
+ (void *)0x55, &cb_ts);
+
+ // Assume there is a next_cb_ts on rstream.
+ rstream_.next_cb_ts.tv_sec = 1;
+ rstream_.next_cb_ts.tv_nsec = 0;
+
+ // Case 1: Not enough samples. Time is not late enough.
+ // Stream can not send data to client.
+ clock_gettime_retspec.tv_sec = 0;
+ clock_gettime_retspec.tv_nsec = 0;
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(0, cras_rstream_audio_ready_called);
+ EXPECT_EQ(0, rc);
+
+ // Case 2: Not enough samples. Time is late enough.
+ // Stream can not send data to client.
+
+ // Assume time is greater than next_cb_ts.
+ clock_gettime_retspec.tv_sec = 1;
+ clock_gettime_retspec.tv_nsec = 500;
+ // However, written frames is less than cb_threshold.
+ // Stream still can not send samples to client.
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(0, cras_rstream_audio_ready_called);
+ EXPECT_EQ(0, rc);
+
+ // Case 3: Enough samples. Time is not late enough.
+ // Stream can not send data to client.
+
+ // Assume time is less than next_cb_ts.
+ clock_gettime_retspec.tv_sec = 0;
+ clock_gettime_retspec.tv_nsec = 0;
+ // Enough samples are written.
+ written_frames = rstream_.cb_threshold + 10;
+ cras_shm_buffer_written(&rstream_.shm, written_frames);
+ // Stream still can not send samples to client.
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(0, cras_rstream_audio_ready_called);
+ EXPECT_EQ(0, rc);
+
+ // Case 4: Enough samples. Time is late enough.
+ // Stream should send one cb_threshold to client.
+ clock_gettime_retspec.tv_sec = 1;
+ clock_gettime_retspec.tv_nsec = 500;
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(1, cras_rstream_audio_ready_called);
+ EXPECT_EQ(rstream_.cb_threshold, cras_rstream_audio_ready_count);
+ EXPECT_EQ(0, rc);
+
+ // Check next_cb_ts is increased by one sleep interval.
+ expected_next_cb_ts.tv_sec = 1;
+ expected_next_cb_ts.tv_nsec = 0;
+ add_timespecs(&expected_next_cb_ts,
+ &rstream_.sleep_interval_ts);
+ EXPECT_EQ(expected_next_cb_ts.tv_sec, rstream_.next_cb_ts.tv_sec);
+ EXPECT_EQ(expected_next_cb_ts.tv_nsec, rstream_.next_cb_ts.tv_nsec);
+
+ // Reset stub data of interest.
+ cras_rstream_audio_ready_called = 0;
+ cras_rstream_audio_ready_count = 0;
+ rstream_.next_cb_ts.tv_sec = 1;
+ rstream_.next_cb_ts.tv_nsec = 0;
+
+ // Case 5: Enough samples. Time is late enough and it is too late
+ // such that a new next_cb_ts is in the past.
+ // Stream should send one cb_threshold to client and reset schedule.
+ clock_gettime_retspec.tv_sec = 2;
+ clock_gettime_retspec.tv_nsec = 0;
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(1, cras_rstream_audio_ready_called);
+ EXPECT_EQ(rstream_.cb_threshold, cras_rstream_audio_ready_count);
+ EXPECT_EQ(0, rc);
+
+ // Check next_cb_ts is rest to be now plus one sleep interval.
+ expected_next_cb_ts.tv_sec = 2;
+ expected_next_cb_ts.tv_nsec = 0;
+ add_timespecs(&expected_next_cb_ts,
+ &rstream_.sleep_interval_ts);
+ EXPECT_EQ(expected_next_cb_ts.tv_sec, rstream_.next_cb_ts.tv_sec);
+ EXPECT_EQ(expected_next_cb_ts.tv_nsec, rstream_.next_cb_ts.tv_nsec);
+
+ dev_stream_destroy(dev_stream);
+}
+
+TEST_F(CreateSuite, StreamCanSendBulkAudio) {
+ struct dev_stream *dev_stream;
+ unsigned int dev_id = 9;
+ int written_frames;
+ int rc;
+ struct timespec expected_next_cb_ts;
+
+ rstream_.direction = CRAS_STREAM_INPUT;
+ rstream_.flags |= BULK_AUDIO_OK;
+ dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
+ (void *)0x55, &cb_ts);
+
+ // Assume there is a next_cb_ts on rstream.
+ rstream_.next_cb_ts.tv_sec = 1;
+ rstream_.next_cb_ts.tv_nsec = 0;
+
+ // Case 1: Not enough samples. Time is not late enough.
+ // Bulk audio stream can not send data to client.
+ clock_gettime_retspec.tv_sec = 0;
+ clock_gettime_retspec.tv_nsec = 0;
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(0, cras_rstream_audio_ready_called);
+ EXPECT_EQ(0, rc);
+
+ // Case 2: Not enough samples. Time is late enough.
+ // Bulk audio stream can not send data to client.
+
+ // Assume time is greater than next_cb_ts.
+ clock_gettime_retspec.tv_sec = 1;
+ clock_gettime_retspec.tv_nsec = 500;
+ // However, written frames is less than cb_threshold.
+ // Stream still can not send samples to client.
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(0, cras_rstream_audio_ready_called);
+ EXPECT_EQ(0, rc);
+
+ // Case 3: Enough samples. Time is not late enough.
+ // Bulk audio stream CAN send data to client.
+
+ // Assume time is less than next_cb_ts.
+ clock_gettime_retspec.tv_sec = 0;
+ clock_gettime_retspec.tv_nsec = 0;
+ // Enough samples are written.
+ written_frames = rstream_.cb_threshold + 10;
+ cras_shm_buffer_written(&rstream_.shm, written_frames);
+ // Bulk audio stream can send all written samples to client.
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(1, cras_rstream_audio_ready_called);
+ EXPECT_EQ(written_frames, cras_rstream_audio_ready_count);
+ EXPECT_EQ(0, rc);
+
+ // Case 4: Enough samples. Time is late enough.
+ // Bulk audio stream can send all written samples to client.
+
+ // Reset stub data of interest.
+ cras_rstream_audio_ready_called = 0;
+ cras_rstream_audio_ready_count = 0;
+ rstream_.next_cb_ts.tv_sec = 1;
+ rstream_.next_cb_ts.tv_nsec = 0;
+
+ clock_gettime_retspec.tv_sec = 1;
+ clock_gettime_retspec.tv_nsec = 500;
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(1, cras_rstream_audio_ready_called);
+ EXPECT_EQ(written_frames, cras_rstream_audio_ready_count);
+ EXPECT_EQ(0, rc);
+
+ // Check next_cb_ts is increased by one sleep interval.
+ expected_next_cb_ts.tv_sec = 1;
+ expected_next_cb_ts.tv_nsec = 0;
+ add_timespecs(&expected_next_cb_ts,
+ &rstream_.sleep_interval_ts);
+ EXPECT_EQ(expected_next_cb_ts.tv_sec, rstream_.next_cb_ts.tv_sec);
+ EXPECT_EQ(expected_next_cb_ts.tv_nsec, rstream_.next_cb_ts.tv_nsec);
+
+ dev_stream_destroy(dev_stream);
+}
+
+TEST_F(CreateSuite, InputDevStreamWakeTimeByNextCbTs) {
+ struct dev_stream *dev_stream;
+ unsigned int dev_id = 9;
+ int rc;
+ unsigned int curr_level = 0;
+ int written_frames;
+ struct timespec level_tstamp = {.tv_sec = 1, .tv_nsec = 0};
+ struct timespec wake_time_out = {.tv_sec = 0, .tv_nsec = 0};
+
+ rstream_.direction = CRAS_STREAM_INPUT;
+ dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
+ (void *)0x55, &cb_ts);
+
+ // Assume there is a next_cb_ts on rstream.
+ rstream_.next_cb_ts.tv_sec = 1;
+ rstream_.next_cb_ts.tv_nsec = 500000;
+
+ // Assume there are enough samples for stream.
+ written_frames = rstream_.cb_threshold + 10;
+ cras_shm_buffer_written(&rstream_.shm, written_frames);
+
+ rc = dev_stream_wake_time(dev_stream, curr_level,
+ &level_tstamp, &wake_time_out);
+
+ // The next wake up time is determined by next_cb_ts on dev_stream.
+ EXPECT_EQ(rstream_.next_cb_ts.tv_sec, wake_time_out.tv_sec);
+ EXPECT_EQ(rstream_.next_cb_ts.tv_nsec, wake_time_out.tv_nsec);
+ EXPECT_EQ(0, rc);
+}
+
+TEST_F(CreateSuite, InputDevStreamWakeTimeByDevice) {
+ struct dev_stream *dev_stream;
+ unsigned int dev_id = 9;
+ int rc;
+ unsigned int curr_level = 100;
+ int written_frames;
+ struct timespec level_tstamp = {.tv_sec = 1, .tv_nsec = 0};
+ struct timespec wake_time_out = {.tv_sec = 0, .tv_nsec = 0};
+ struct timespec expected_tstamp = {.tv_sec = 0, .tv_nsec = 0};
+ struct timespec needed_time_for_device = {.tv_sec = 0, .tv_nsec = 0};
+ int needed_frames_from_device = 0;
+
+ rstream_.direction = CRAS_STREAM_INPUT;
+ dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_48,
+ (void *)0x55, &cb_ts);
+
+ // Assume there is a next_cb_ts on rstream, that is, 1.005 seconds.
+ rstream_.next_cb_ts.tv_sec = 1;
+ rstream_.next_cb_ts.tv_nsec = 5000000; // 5ms
+
+ // Assume there are not enough samples for stream.
+ written_frames = 123;
+ cras_shm_buffer_written(&rstream_.shm, written_frames);
+
+ // Compute wake up time for device level to reach enough samples
+ // for one cb_threshold:
+ // Device has 100 samples (48K rate).
+ // Stream has 123 samples (44.1K rate)
+ // cb_threshold = 512 samples.
+ // Stream needs 512 - 123 = 389 samples.
+ // Converted to device rate => 389 * 48000.0 / 44100 = 423.4 samples
+ // => 424 samples.
+ // Device needs another 424 - 100 = 324 samples.
+ // Time for 252 samples = 324 / 48000 = 0.00675 sec.
+ // So expected wake up time for samples is at level_tstamp + 0.00675 sec =
+ // 1.00675 seconds.
+ needed_frames_from_device = cras_frames_at_rate(
+ 44100, rstream_.cb_threshold - written_frames, 48000);
+ needed_frames_from_device -= curr_level;
+ cras_frames_to_time(needed_frames_from_device, 48000,
+ &needed_time_for_device);
+
+ expected_tstamp.tv_sec = level_tstamp.tv_sec;
+ expected_tstamp.tv_nsec = level_tstamp.tv_nsec;
+
+ add_timespecs(&expected_tstamp, &needed_time_for_device);
+
+ // Set the stub data for cras_fmt_conv_out_frames_to_in.
+ out_fmt.frame_rate = 44100;
+ in_fmt.frame_rate = 48000;
+
+ rc = dev_stream_wake_time(dev_stream, curr_level,
+ &level_tstamp, &wake_time_out);
+
+ // The next wake up time is determined by needed time for device level
+ // to reach enough samples for one cb_threshold.
+ EXPECT_EQ(expected_tstamp.tv_sec, wake_time_out.tv_sec);
+ EXPECT_EQ(expected_tstamp.tv_nsec, wake_time_out.tv_nsec);
+ EXPECT_EQ(0, rc);
+
+ // Assume current level is larger than cb_threshold.
+ // The wake up time is determined by next_cb_ts.
+ curr_level += rstream_.cb_threshold;
+ rc = dev_stream_wake_time(dev_stream, curr_level,
+ &level_tstamp, &wake_time_out);
+ EXPECT_EQ(rstream_.next_cb_ts.tv_sec, wake_time_out.tv_sec);
+ EXPECT_EQ(rstream_.next_cb_ts.tv_nsec, wake_time_out.tv_nsec);
+ EXPECT_EQ(0, rc);
+}
+
// Test set_playback_timestamp.
TEST(DevStreamTimimg, SetPlaybackTimeStampSimple) {
struct cras_timespec ts;
@@ -622,10 +879,13 @@ TEST(DevStreamTimimg, SetCaptureTimeStampWrapPartial) {
extern "C" {
int cras_rstream_audio_ready(struct cras_rstream *stream, size_t count) {
+ cras_rstream_audio_ready_count = count;
+ cras_rstream_audio_ready_called++;
return 0;
}
-int cras_rstream_request_audio(const struct cras_rstream *stream) {
+int cras_rstream_request_audio(struct cras_rstream *stream,
+ const struct timespec *now) {
return 0;
}
@@ -743,16 +1003,16 @@ void cras_audio_area_config_channels(struct cras_audio_area *area,
unsigned int cras_audio_area_copy(const struct cras_audio_area *dst,
unsigned int dst_offset,
- const struct cras_audio_format *dst_fmt,
+ const struct cras_audio_format *dst_fmt,
const struct cras_audio_area *src,
unsigned int src_offset,
- unsigned int src_index) {
+ float software_gain_scaler) {
copy_area_call.dst = dst;
copy_area_call.dst_offset = dst_offset;
copy_area_call.dst_format_bytes = cras_get_format_bytes(dst_fmt);
copy_area_call.src = src;
copy_area_call.src_offset = src_offset;
- copy_area_call.src_index = src_index;
+ copy_area_call.software_gain_scaler = software_gain_scaler;
return src->frames;
}
diff --git a/cras/src/tests/device_blacklist_unittest.cc b/cras/src/tests/device_blacklist_unittest.cc
index 9e93a4e9..812ac4cc 100644
--- a/cras/src/tests/device_blacklist_unittest.cc
+++ b/cras/src/tests/device_blacklist_unittest.cc
@@ -11,7 +11,7 @@ extern "C" {
namespace {
-static const char CONFIG_PATH[] = "/tmp";
+static const char CONFIG_PATH[] = CRAS_UT_TMPDIR;
static const char CONFIG_FILENAME[] = "device_blacklist";
void CreateConfigFile(const char* config_text) {
diff --git a/cras/src/tests/device_monitor_unittest.cc b/cras/src/tests/device_monitor_unittest.cc
new file mode 100644
index 00000000..31913beb
--- /dev/null
+++ b/cras/src/tests/device_monitor_unittest.cc
@@ -0,0 +1,154 @@
+// Copyright 2016 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "cras_device_monitor.c"
+#include "cras_iodev.h"
+#include "cras_main_message.h"
+}
+
+static enum CRAS_MAIN_MESSAGE_TYPE type_set;
+static struct cras_device_monitor_message *sent_msg;
+static int enable_dev_called;
+static cras_iodev *enable_dev;
+static int disable_dev_called;
+static cras_iodev *disable_dev;
+static int set_mute_called;
+static cras_iodev *mute_dev;
+
+void ResetStubData() {
+ type_set = (enum CRAS_MAIN_MESSAGE_TYPE)0;
+ enable_dev_called = 0;
+ enable_dev = NULL;
+ disable_dev_called = 0;
+ disable_dev = NULL;
+ set_mute_called = 0;
+ mute_dev = NULL;
+}
+
+namespace {
+
+TEST(DeviceMonitorTestSuite, Init) {
+ ResetStubData();
+
+ cras_device_monitor_init();
+
+ EXPECT_EQ(type_set, CRAS_MAIN_MONITOR_DEVICE);
+}
+
+TEST(DeviceMonitorTestSuite, ResetDevice) {
+ struct cras_iodev dev;
+ ResetStubData();
+ // sent_msg will be filled with message content in cras_main_message_send.
+ sent_msg = (struct cras_device_monitor_message *)calloc(1, sizeof(*sent_msg));
+
+ cras_device_monitor_reset_device(&dev);
+
+ EXPECT_EQ(sent_msg->header.type, CRAS_MAIN_MONITOR_DEVICE);
+ EXPECT_EQ(sent_msg->header.length, sizeof(*sent_msg));
+ EXPECT_EQ(sent_msg->message_type, RESET_DEVICE);
+ EXPECT_EQ(sent_msg->iodev, &dev);
+
+ free(sent_msg);
+}
+
+TEST(DeviceMonitorTestSuite, HandleResetDevice) {
+ struct cras_iodev dev;
+ struct cras_device_monitor_message msg;
+ struct cras_main_message *main_message =
+ reinterpret_cast<struct cras_main_message *>(&msg);
+
+ ResetStubData();
+
+ // Filled msg with message content for resetting device.
+ init_device_msg(&msg, RESET_DEVICE, &dev);
+ // Assume the pipe works fine and main message handler receives the same
+ // message.
+ handle_device_message(main_message, NULL);
+
+ // Verify that disable/enable functions are called with correct device.
+ EXPECT_EQ(enable_dev_called, 1);
+ EXPECT_EQ(enable_dev, &dev);
+ EXPECT_EQ(disable_dev_called, 1);
+ EXPECT_EQ(disable_dev, &dev);
+}
+
+TEST(DeviceMonitorTestSuite, MuteDevice) {
+ struct cras_iodev dev;
+ ResetStubData();
+ // sent_msg will be filled with message content in cras_main_message_send.
+ sent_msg = (struct cras_device_monitor_message *)calloc(1, sizeof(*sent_msg));
+
+ cras_device_monitor_set_device_mute_state(&dev);
+
+ EXPECT_EQ(sent_msg->header.type, CRAS_MAIN_MONITOR_DEVICE);
+ EXPECT_EQ(sent_msg->header.length, sizeof(*sent_msg));
+ EXPECT_EQ(sent_msg->message_type, SET_MUTE_STATE);
+ EXPECT_EQ(sent_msg->iodev, &dev);
+
+ free(sent_msg);
+}
+
+TEST(DeviceMonitorTestSuite, HandleMuteDevice) {
+ struct cras_iodev dev;
+ struct cras_device_monitor_message msg;
+ struct cras_main_message *main_message =
+ reinterpret_cast<struct cras_main_message *>(&msg);
+
+ ResetStubData();
+
+ // Filled msg with message content for device mute/unmute.
+ init_device_msg(&msg, SET_MUTE_STATE, &dev);
+ // Assume the pipe works fine and main message handler receives the same
+ // message.
+ handle_device_message(main_message, NULL);
+
+ // Verify that cras_iodev_set_mute is called with correct device.
+ EXPECT_EQ(set_mute_called, 1);
+ EXPECT_EQ(mute_dev, &dev);
+}
+
+extern "C" {
+
+int cras_main_message_add_handler(enum CRAS_MAIN_MESSAGE_TYPE type,
+ cras_message_callback callback,
+ void *callback_data) {
+ type_set = type;
+ return 0;
+}
+
+int cras_main_message_send(struct cras_main_message *msg) {
+ // Copy the sent message so we can examine it in the test later.
+ memcpy(sent_msg, msg, sizeof(*sent_msg));
+ return 0;
+};
+
+void cras_iodev_list_enable_dev(struct cras_iodev *dev) {
+ enable_dev_called++;
+ enable_dev = dev;
+}
+
+void cras_iodev_list_disable_dev(struct cras_iodev *dev) {
+ disable_dev_called++;
+ disable_dev = dev;
+}
+
+int cras_iodev_set_mute(struct cras_iodev *dev) {
+ set_mute_called++;
+ mute_dev = dev;
+ return 0;
+}
+
+} // extern "C"
+} // namespace
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int rc = RUN_ALL_TESTS();
+
+ return rc;
+}
diff --git a/cras/src/tests/dsp_ini_unittest.cc b/cras/src/tests/dsp_ini_unittest.cc
index d27c6937..b7f488de 100644
--- a/cras/src/tests/dsp_ini_unittest.cc
+++ b/cras/src/tests/dsp_ini_unittest.cc
@@ -174,6 +174,133 @@ TEST_F(DspIniTestSuite, Flows) {
cras_dsp_ini_free(ini);
}
+TEST_F(DspIniTestSuite, TwoChannelWithSwap) {
+
+ /*
+ * Stated in ini:
+ *
+ * m0 ==(a0, a1)== m1 ==(b0, b1)== m2
+ *
+ * After inserting swap_lr plugin:
+ *
+ * m0 ==(a0, a1)== m1 ==(b0, b1)== m_swap_lr ==(swap_lr_0, swap_lr_1)== m2
+ *
+ */
+
+ const char *content =
+ "[M0]\n"
+ "library=builtin\n"
+ "label=source\n"
+ "purpose=playback\n"
+ "output_0={a0}\n"
+ "output_1={a1}\n"
+ "[M1]\n"
+ "library=builtin\n"
+ "label=foo\n"
+ "purpose=playback\n"
+ "input_0={a0}\n"
+ "input_1={a1}\n"
+ "output_2={b0}\n"
+ "output_3={b1}\n"
+ "[M2]\n"
+ "library=builtin\n"
+ "label=sink\n"
+ "purpose=playback\n"
+ "input_0={b0}\n"
+ "input_1={b1}\n";
+ fprintf(fp, "%s", content);
+ CloseFile();
+
+ struct ini *ini = cras_dsp_ini_create(filename);
+
+ /* 3 plugins and 1 swap_lr plugin. */
+ EXPECT_EQ(4, ARRAY_COUNT(&ini->plugins));
+
+ struct plugin *m0= ARRAY_ELEMENT(&ini->plugins, 0);
+ struct plugin *m1 = ARRAY_ELEMENT(&ini->plugins, 1);
+ struct plugin *m2 = ARRAY_ELEMENT(&ini->plugins, 2);
+ struct plugin *m_swap_lr = ARRAY_ELEMENT(&ini->plugins, 3);
+
+ EXPECT_EQ(2, ARRAY_COUNT(&m0->ports));
+ EXPECT_EQ(4, ARRAY_COUNT(&m1->ports));
+ EXPECT_EQ(4, ARRAY_COUNT(&m_swap_lr->ports));
+ EXPECT_EQ(2, ARRAY_COUNT(&m2->ports));
+
+ struct port *m0_0 = ARRAY_ELEMENT(&m0->ports, 0);
+ struct port *m0_1 = ARRAY_ELEMENT(&m0->ports, 1);
+ struct port *m1_0 = ARRAY_ELEMENT(&m1->ports, 0);
+ struct port *m1_1 = ARRAY_ELEMENT(&m1->ports, 1);
+ struct port *m1_2 = ARRAY_ELEMENT(&m1->ports, 2);
+ struct port *m1_3 = ARRAY_ELEMENT(&m1->ports, 3);
+ struct port *m_swap_lr_0 = ARRAY_ELEMENT(&m_swap_lr->ports, 0);
+ struct port *m_swap_lr_1 = ARRAY_ELEMENT(&m_swap_lr->ports, 1);
+ struct port *m_swap_lr_2 = ARRAY_ELEMENT(&m_swap_lr->ports, 2);
+ struct port *m_swap_lr_3 = ARRAY_ELEMENT(&m_swap_lr->ports, 3);
+ struct port *m2_0 = ARRAY_ELEMENT(&m2->ports, 0);
+ struct port *m2_1 = ARRAY_ELEMENT(&m2->ports, 1);
+
+ /* flow flow_id from port to port
+ * ------------------------------------------------------------
+ * a0 0 m0_0 m1_0
+ * a1 1 m0_1 m1_1
+ * b0 2 m1_2 m_swap_lr_0
+ * b1 3 m1_3 m_swap_lr_1
+ * swap_lr_0 4 m_swap_lr_2 m2_0
+ * swap_lr_1 5 m_swap_lr_3 m2_1
+ */
+ EXPECT_EQ(0, m0_0->flow_id);
+ EXPECT_EQ(1, m0_1->flow_id);
+ EXPECT_EQ(0, m1_0->flow_id);
+ EXPECT_EQ(1, m1_1->flow_id);
+ EXPECT_EQ(2, m1_2->flow_id);
+ EXPECT_EQ(3, m1_3->flow_id);
+ EXPECT_EQ(2, m_swap_lr_0->flow_id);
+ EXPECT_EQ(3, m_swap_lr_1->flow_id);
+ EXPECT_EQ(4, m_swap_lr_2->flow_id);
+ EXPECT_EQ(5, m_swap_lr_3->flow_id);
+ EXPECT_EQ(4, m2_0->flow_id);
+ EXPECT_EQ(5, m2_1->flow_id);
+
+ struct flow *flow_a0 = ARRAY_ELEMENT(&ini->flows, 0);
+ struct flow *flow_a1 = ARRAY_ELEMENT(&ini->flows, 1);
+ struct flow *flow_b0 = ARRAY_ELEMENT(&ini->flows, 2);
+ struct flow *flow_b1 = ARRAY_ELEMENT(&ini->flows, 3);
+ struct flow *flow_swap_lr_0 = ARRAY_ELEMENT(&ini->flows, 4);
+ struct flow *flow_swap_lr_1 = ARRAY_ELEMENT(&ini->flows, 5);
+
+ EXPECT_EQ(flow_a0->from, m0);
+ EXPECT_EQ(flow_a0->from_port, 0);
+ EXPECT_EQ(flow_a0->to, m1);
+ EXPECT_EQ(flow_a0->to_port, 0);
+
+ EXPECT_EQ(flow_a1->from, m0);
+ EXPECT_EQ(flow_a1->from_port, 1);
+ EXPECT_EQ(flow_a1->to, m1);
+ EXPECT_EQ(flow_a1->to_port, 1);
+
+ EXPECT_EQ(flow_b0->from, m1);
+ EXPECT_EQ(flow_b0->from_port, 2);
+ EXPECT_EQ(flow_b0->to, m_swap_lr);
+ EXPECT_EQ(flow_b0->to_port, 0);
+
+ EXPECT_EQ(flow_b1->from, m1);
+ EXPECT_EQ(flow_b1->from_port, 3);
+ EXPECT_EQ(flow_b1->to, m_swap_lr);
+ EXPECT_EQ(flow_b1->to_port, 1);
+
+ EXPECT_EQ(flow_swap_lr_0->from, m_swap_lr);
+ EXPECT_EQ(flow_swap_lr_0->from_port, 2);
+ EXPECT_EQ(flow_swap_lr_0->to, m2);
+ EXPECT_EQ(flow_swap_lr_0->to_port, 0);
+
+ EXPECT_EQ(flow_swap_lr_1->from, m_swap_lr);
+ EXPECT_EQ(flow_swap_lr_1->from_port, 3);
+ EXPECT_EQ(flow_swap_lr_1->to, m2);
+ EXPECT_EQ(flow_swap_lr_1->to_port, 1);
+
+ cras_dsp_ini_free(ini);
+}
+
} // namespace
int main(int argc, char **argv) {
diff --git a/cras/src/tests/dsp_unittest.cc b/cras/src/tests/dsp_unittest.cc
index a8a03d72..40fd9dab 100644
--- a/cras/src/tests/dsp_unittest.cc
+++ b/cras/src/tests/dsp_unittest.cc
@@ -65,9 +65,9 @@ TEST_F(DspTestSuite, Simple) {
ctx3 = cras_dsp_context_new(44100, "capture");
ctx4 = cras_dsp_context_new(44100, "capture");
- cras_dsp_set_variable(ctx1, "variable", "foo");
- cras_dsp_set_variable(ctx3, "variable", "bar"); /* wrong value */
- cras_dsp_set_variable(ctx4, "variable", "foo");
+ cras_dsp_set_variable_string(ctx1, "variable", "foo");
+ cras_dsp_set_variable_string(ctx3, "variable", "bar"); /* wrong value */
+ cras_dsp_set_variable_string(ctx4, "variable", "foo");
cras_dsp_load_pipeline(ctx1);
cras_dsp_load_pipeline(ctx3);
@@ -82,12 +82,12 @@ TEST_F(DspTestSuite, Simple) {
cras_dsp_put_pipeline(ctx4);
/* change the variable to a wrong value, and we should fail to reload. */
- cras_dsp_set_variable(ctx4, "variable", "bar");
+ cras_dsp_set_variable_string(ctx4, "variable", "bar");
cras_dsp_load_pipeline(ctx4);
ASSERT_EQ(NULL, cras_dsp_get_pipeline(ctx4));
/* change the variable back, and we should reload successfully. */
- cras_dsp_set_variable(ctx4, "variable", "foo");
+ cras_dsp_set_variable_string(ctx4, "variable", "foo");
cras_dsp_reload_ini();
ASSERT_TRUE(cras_dsp_get_pipeline(ctx4));
diff --git a/cras/src/tests/file_wait_unittest.cc b/cras/src/tests/file_wait_unittest.cc
new file mode 100644
index 00000000..6bd76f32
--- /dev/null
+++ b/cras/src/tests/file_wait_unittest.cc
@@ -0,0 +1,279 @@
+// Copyright 2016 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <gtest/gtest.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string>
+
+#include "cras_util.h"
+#include "cras_file_wait.h"
+
+extern "C" {
+// This function is not exported in cras_util.h.
+void cras_file_wait_mock_race_condition(struct cras_file_wait *file_wait);
+}
+
+namespace {
+
+// Executes "rm -rf <path>".
+static int RmRF(const std::string& path) {
+ std::string cmd("rm -rf \"");
+ cmd += path + "\"";
+
+ if (path == "/")
+ return -EINVAL;
+
+ int rc = system(cmd.c_str());
+ if (rc < 0)
+ return -errno;
+ return WEXITSTATUS(rc);
+}
+
+// Filled-in by the FileWaitCallback.
+struct FileWaitResult {
+ size_t called;
+ cras_file_wait_event_t event;
+};
+
+// Called by the file wait code for an event.
+static void FileWaitCallback(void *context,
+ cras_file_wait_event_t event,
+ const char *filename)
+{
+ FileWaitResult *result = reinterpret_cast<FileWaitResult*>(context);
+ result->called++;
+ result->event = event;
+}
+
+// Do all of the EXPECTed steps for a simple wait for one file.
+static void SimpleFileWait(const char *file_path) {
+ struct cras_file_wait *file_wait;
+ FileWaitResult file_wait_result;
+ struct pollfd poll_fd;
+ struct timespec timeout = {0, 100000000};
+ struct stat stat_buf;
+ int stat_rc;
+
+ stat_rc = stat(file_path, &stat_buf);
+ if (stat_rc < 0)
+ stat_rc = -errno;
+
+ file_wait_result.called = 0;
+ EXPECT_EQ(0, cras_file_wait_create(file_path, CRAS_FILE_WAIT_FLAG_NONE,
+ FileWaitCallback, &file_wait_result,
+ &file_wait));
+ EXPECT_NE(reinterpret_cast<struct cras_file_wait *>(NULL), file_wait);
+ if (stat_rc == 0) {
+ EXPECT_EQ(1, file_wait_result.called);
+ EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
+ } else {
+ EXPECT_EQ(0, file_wait_result.called);
+ }
+ poll_fd.events = POLLIN;
+ poll_fd.fd = cras_file_wait_get_fd(file_wait);
+
+ file_wait_result.called = 0;
+ if (stat_rc == 0)
+ EXPECT_EQ(0, RmRF(file_path));
+ else
+ EXPECT_EQ(0, mknod(file_path, S_IFREG | 0600, 0));
+ EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
+ EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
+ EXPECT_EQ(1, file_wait_result.called);
+ if (stat_rc == 0)
+ EXPECT_EQ(CRAS_FILE_WAIT_EVENT_DELETED, file_wait_result.event);
+ else
+ EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
+ EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
+ cras_file_wait_destroy(file_wait);
+}
+
+// Test the cras_file_wait functions including multiple path components
+// missing and path components deleted and recreated.
+TEST(Util, FileWait) {
+ struct cras_file_wait *file_wait;
+ FileWaitResult file_wait_result;
+ pid_t pid = getpid();
+ struct pollfd poll_fd;
+ int current_dir;
+ struct timespec timeout = {0, 100000000};
+ char pid_buf[32];
+ std::string tmp_dir(CRAS_UT_TMPDIR);
+ std::string dir_path;
+ std::string subdir_path;
+ std::string file_path;
+
+ snprintf(pid_buf, sizeof(pid_buf), "%d", pid);
+ dir_path = tmp_dir + "/" + pid_buf;
+ subdir_path = dir_path + "/subdir";
+ file_path = subdir_path + "/does_not_exist";
+
+ // Test arguments.
+ // Null file path.
+ EXPECT_EQ(-EINVAL, cras_file_wait_create(
+ NULL, CRAS_FILE_WAIT_FLAG_NONE,
+ FileWaitCallback, &file_wait_result, &file_wait));
+ // Empty file path.
+ EXPECT_EQ(-EINVAL, cras_file_wait_create(
+ "", CRAS_FILE_WAIT_FLAG_NONE,
+ FileWaitCallback, &file_wait_result, &file_wait));
+ // No callback structure.
+ EXPECT_EQ(-EINVAL, cras_file_wait_create(
+ ".", CRAS_FILE_WAIT_FLAG_NONE,
+ NULL, NULL, &file_wait));
+ // No file wait structure.
+ EXPECT_EQ(-EINVAL, cras_file_wait_create(
+ ".", CRAS_FILE_WAIT_FLAG_NONE,
+ FileWaitCallback, &file_wait_result, NULL));
+ EXPECT_EQ(-EINVAL, cras_file_wait_dispatch(NULL));
+ EXPECT_EQ(-EINVAL, cras_file_wait_get_fd(NULL));
+
+ // Make sure that /tmp exists.
+ file_wait_result.called = 0;
+ EXPECT_EQ(0, cras_file_wait_create(CRAS_UT_TMPDIR, CRAS_FILE_WAIT_FLAG_NONE,
+ FileWaitCallback, &file_wait_result,
+ &file_wait));
+ EXPECT_NE(reinterpret_cast<struct cras_file_wait *>(NULL), file_wait);
+ EXPECT_EQ(file_wait_result.called, 1);
+ ASSERT_EQ(file_wait_result.event, CRAS_FILE_WAIT_EVENT_CREATED);
+ cras_file_wait_destroy(file_wait);
+
+ // Create our temporary dir.
+ ASSERT_EQ(0, RmRF(dir_path));
+ ASSERT_EQ(0, mkdir(dir_path.c_str(), 0700));
+
+ // Start looking for our file '.../does_not_exist'.
+ EXPECT_EQ(0, cras_file_wait_create(file_path.c_str(),
+ CRAS_FILE_WAIT_FLAG_NONE,
+ FileWaitCallback, &file_wait_result,
+ &file_wait));
+ EXPECT_NE(reinterpret_cast<struct cras_file_wait *>(NULL), file_wait);
+ poll_fd.events = POLLIN;
+ poll_fd.fd = cras_file_wait_get_fd(file_wait);
+ EXPECT_NE(0, poll_fd.fd >= 0);
+
+ // Create a sub-directory in the path.
+ file_wait_result.called = 0;
+ EXPECT_EQ(0, mkdir(subdir_path.c_str(), 0700));
+ EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
+ EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
+ EXPECT_EQ(0, file_wait_result.called);
+ // Removing a watch causes generation of an IN_IGNORED event for the previous
+ // watch_id. cras_file_wait_dispatch will ignore this and return 0.
+ EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
+ EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
+
+ // Remove the directory that we're watching.
+ EXPECT_EQ(0, RmRF(subdir_path));
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 100000000;
+ EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
+ EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
+ EXPECT_EQ(0, file_wait_result.called);
+ EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
+
+ // Create a sub-directory in the path (again).
+ EXPECT_EQ(0, mkdir(subdir_path.c_str(), 0700));
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 100000000;
+ EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
+ EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
+ EXPECT_EQ(0, file_wait_result.called);
+ // See IN_IGNORED above.
+ EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
+ EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
+
+ // Create the file we're looking for.
+ EXPECT_EQ(0, mknod(file_path.c_str(), S_IFREG | 0600, 0));
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 100000000;
+ EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
+ EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
+ EXPECT_EQ(1, file_wait_result.called);
+ EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
+ EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
+
+ // Remove the file.
+ file_wait_result.called = 0;
+ EXPECT_EQ(0, unlink(file_path.c_str()));
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 100000000;
+ EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
+ EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
+ EXPECT_EQ(1, file_wait_result.called);
+ EXPECT_EQ(CRAS_FILE_WAIT_EVENT_DELETED, file_wait_result.event);
+ EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
+
+ // Re-create the file.
+ file_wait_result.called = 0;
+ EXPECT_EQ(0, mknod(file_path.c_str(), S_IFREG | 0600, 0));
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 100000000;
+ EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
+ EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
+ EXPECT_EQ(1, file_wait_result.called);
+ EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
+ EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
+
+ // Remove the subdir.
+ file_wait_result.called = 0;
+ EXPECT_EQ(0, RmRF(subdir_path));
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 100000000;
+ EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
+ EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
+ EXPECT_EQ(1, file_wait_result.called);
+ EXPECT_EQ(CRAS_FILE_WAIT_EVENT_DELETED, file_wait_result.event);
+ EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
+
+ // Create a sub-directory in the path (again), and this time mock a race
+ // condition for creation of the file.
+ file_wait_result.called = 0;
+ EXPECT_EQ(0, mkdir(subdir_path.c_str(), 0700));
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 100000000;
+ EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
+ cras_file_wait_mock_race_condition(file_wait);
+ EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
+ EXPECT_EQ(1, file_wait_result.called);
+ EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
+ EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
+ EXPECT_EQ(1, file_wait_result.called);
+ EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
+
+ // Cleanup.
+ cras_file_wait_destroy(file_wait);
+
+ // Treat consecutive '/' as one.
+ file_path = dir_path + "//does_not_exist_too";
+ SimpleFileWait(file_path.c_str());
+
+ // Stash the current directory.
+ current_dir = open(".", O_RDONLY|O_PATH|O_DIRECTORY);
+ ASSERT_NE(0, current_dir >= 0);
+
+ // Search for a file in the current directory.
+ ASSERT_EQ(0, chdir(dir_path.c_str()));
+ SimpleFileWait("does_not_exist_either");
+
+ // Test notification of deletion in the current directory.
+ SimpleFileWait("does_not_exist_either");
+
+ // Search for a file in the current directory (variation).
+ SimpleFileWait("./does_not_exist_either_too");
+
+ // Return to the start directory.
+ EXPECT_EQ(0, fchdir(current_dir));
+
+ // Clean up.
+ EXPECT_EQ(0, RmRF(dir_path));
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/cras/src/tests/fmt_conv_unittest.cc b/cras/src/tests/fmt_conv_unittest.cc
index 3026cc57..a0b6194e 100644
--- a/cras/src/tests/fmt_conv_unittest.cc
+++ b/cras/src/tests/fmt_conv_unittest.cc
@@ -1222,6 +1222,39 @@ TEST(FormatConverterTest, ConfigConverterNoNeedForInput) {
EXPECT_EQ(0, cras_fmt_conversion_needed(c));
}
+TEST(ChannelRemixTest, ChannelRemixAppliedOrNot) {
+ float coeff[4] = {0.5, 0.5, 0.26, 0.73};
+ struct cras_fmt_conv *conv;
+ struct cras_audio_format fmt;
+ int16_t *buf, *res;
+ unsigned i;
+
+ fmt.num_channels = 2;
+ conv = cras_channel_remix_conv_create(2, coeff);
+
+ buf = (int16_t *)ralloc(50 * 4);
+ res = (int16_t *)malloc(50 * 4);
+
+ for (i = 0; i < 100; i += 2) {
+ res[i] = coeff[0] * buf[i];
+ res[i] += coeff[1] * buf[i + 1];
+ res[i + 1] = coeff[2] * buf[i];
+ res[i + 1] += coeff[3] * buf[i + 1];
+ }
+
+ cras_channel_remix_convert(conv, &fmt, (uint8_t *)buf, 50);
+ for (i = 0; i < 100; i++)
+ EXPECT_EQ(res[i], buf[i]);
+
+ /* If num_channels not match, remix conversion will not apply. */
+ fmt.num_channels = 6;
+ cras_channel_remix_convert(conv, &fmt, (uint8_t *)buf, 50);
+ for (i = 0; i < 100; i++)
+ EXPECT_EQ(res[i], buf[i]);
+
+ cras_fmt_conv_destroy(conv);
+}
+
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
diff --git a/cras/src/tests/hfp_info_unittest.cc b/cras/src/tests/hfp_info_unittest.cc
index 20be29c4..82b93931 100644
--- a/cras/src/tests/hfp_info_unittest.cc
+++ b/cras/src/tests/hfp_info_unittest.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013 The Chromium Authors. All rights reserved.
+/* Copyright 2013 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.
*/
@@ -310,6 +310,11 @@ TEST(HfpInfo, StartHfpInfoAndWrite) {
extern "C" {
+struct audio_thread *cras_iodev_list_get_audio_thread()
+{
+ return NULL;
+}
+
void audio_thread_add_callback(int fd, thread_callback cb,
void *data)
{
@@ -318,11 +323,11 @@ void audio_thread_add_callback(int fd, thread_callback cb,
return;
}
-void audio_thread_rm_callback(int fd)
+int audio_thread_rm_callback_sync(struct audio_thread *thread, int fd)
{
thread_cb = NULL;
cb_data = NULL;
- return;
+ return 0;
}
}
diff --git a/cras/src/tests/hfp_iodev_unittest.cc b/cras/src/tests/hfp_iodev_unittest.cc
index e6b3f777..202bb135 100644
--- a/cras/src/tests/hfp_iodev_unittest.cc
+++ b/cras/src/tests/hfp_iodev_unittest.cc
@@ -71,7 +71,8 @@ void ResetStubData() {
namespace {
-TEST(HfpIodev, CreateHfpIodev) {
+TEST(HfpIodev, CreateHfpOutputIodev) {
+ ResetStubData();
iodev = hfp_iodev_create(CRAS_STREAM_OUTPUT, fake_device, fake_slc,
CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY,
fake_info);
@@ -87,6 +88,24 @@ TEST(HfpIodev, CreateHfpIodev) {
ASSERT_EQ(1, cras_iodev_rm_node_called);
}
+TEST(HfpIodev, CreateHfpInputIodev) {
+ ResetStubData();
+ iodev = hfp_iodev_create(CRAS_STREAM_INPUT, fake_device, fake_slc,
+ CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY, fake_info);
+
+ ASSERT_EQ(CRAS_STREAM_INPUT, iodev->direction);
+ ASSERT_EQ(1, cras_bt_device_append_iodev_called);
+ ASSERT_EQ(1, cras_iodev_add_node_called);
+ ASSERT_EQ(1, cras_iodev_set_active_node_called);
+ /* Input device does not use software gain. */
+ ASSERT_EQ(0, iodev->software_volume_needed);
+
+ hfp_iodev_destroy(iodev);
+
+ ASSERT_EQ(1, cras_bt_device_rm_iodev_called);
+ ASSERT_EQ(1, cras_iodev_rm_node_called);
+}
+
TEST(HfpIodev, OpenHfpIodev) {
ResetStubData();
@@ -105,7 +124,6 @@ TEST(HfpIodev, OpenHfpIodev) {
/* hfp_info is running now */
hfp_info_running_return_val = 1;
- ASSERT_EQ(1, iodev->is_open(iodev));
iodev->close_dev(iodev);
ASSERT_EQ(1, hfp_info_rm_iodev_called);
@@ -130,8 +148,6 @@ TEST(HfpIodev, OpenIodevWithHfpInfoAlreadyRunning) {
ASSERT_EQ(0, hfp_info_start_called);
ASSERT_EQ(1, hfp_info_add_iodev_called);
- ASSERT_EQ(1, iodev->is_open(iodev));
-
hfp_info_has_iodev_return_val = 1;
iodev->close_dev(iodev);
ASSERT_EQ(1, hfp_info_rm_iodev_called);
@@ -210,11 +226,6 @@ const char *cras_bt_device_address(const struct cras_bt_device *device) {
return "1A:2B:3C:4D:5E:6F";
}
-int cras_bt_device_set_speaker_gain(struct cras_bt_device *device, int gain)
-{
- return 0;
-}
-
void cras_bt_device_append_iodev(struct cras_bt_device *device,
struct cras_iodev *iodev,
enum cras_bt_device_profile profile)
@@ -333,6 +344,11 @@ int hfp_set_call_status(struct hfp_slc_handle *handle, int call)
return 0;
}
+int hfp_event_speaker_gain(struct hfp_slc_handle *handle, int gain)
+{
+ return 0;
+}
+
} // extern "C"
int main(int argc, char **argv) {
diff --git a/cras/src/tests/hfp_slc_unittest.cc b/cras/src/tests/hfp_slc_unittest.cc
index 0479f6c5..32749fd8 100644
--- a/cras/src/tests/hfp_slc_unittest.cc
+++ b/cras/src/tests/hfp_slc_unittest.cc
@@ -16,12 +16,15 @@ extern "C" {
static struct hfp_slc_handle *handle;
static struct cras_telephony_handle fake_telephony;
+static int cras_bt_device_update_hardware_volume_called;
static int slc_initialized_cb_called;
static int slc_disconnected_cb_called;
static int cras_system_add_select_fd_called;
static void(*slc_cb)(void *data);
static void *slc_cb_data;
static int fake_errno;
+static struct cras_bt_device *device =
+ reinterpret_cast<struct cras_bt_device *>(2);
int slc_initialized_cb(struct hfp_slc_handle *handle);
int slc_disconnected_cb(struct hfp_slc_handle *handle);
@@ -29,6 +32,7 @@ int slc_disconnected_cb(struct hfp_slc_handle *handle);
void ResetStubData() {
slc_initialized_cb_called = 0;
cras_system_add_select_fd_called = 0;
+ cras_bt_device_update_hardware_volume_called = 0;
slc_cb = NULL;
slc_cb_data = NULL;
}
@@ -38,7 +42,7 @@ namespace {
TEST(HfpSlc, CreateSlcHandle) {
ResetStubData();
- handle = hfp_slc_create(0, 0, slc_initialized_cb,
+ handle = hfp_slc_create(0, 0, device, slc_initialized_cb,
slc_disconnected_cb);
ASSERT_EQ(1, cras_system_add_select_fd_called);
ASSERT_EQ(handle, slc_cb_data);
@@ -54,7 +58,7 @@ TEST(HfpSlc, InitializeSlc) {
ResetStubData();
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sock));
- handle = hfp_slc_create(sock[0], 0, slc_initialized_cb,
+ handle = hfp_slc_create(sock[0], 0, device, slc_initialized_cb,
slc_disconnected_cb);
err = write(sock[1], "AT+CIND=?\r", 10);
@@ -89,6 +93,18 @@ TEST(HfpSlc, InitializeSlc) {
ASSERT_NE((void *)NULL, (void *)chp);
ASSERT_EQ(0, strncmp("\r\nOK", chp, 4));
+ err = write(sock[1], "AT+VGS=13\r", 10);
+ ASSERT_EQ(err, 10);
+ slc_cb(slc_cb_data);
+
+ err = read(sock[1], buf, 256);
+
+ chp = strstr(buf, "\r\n");
+ ASSERT_NE((void *)NULL, (void *)chp);
+ ASSERT_EQ(0, strncmp("\r\nOK", chp, 4));
+
+ ASSERT_EQ(1, cras_bt_device_update_hardware_volume_called);
+
hfp_slc_destroy(handle);
}
@@ -97,7 +113,7 @@ TEST(HfpSlc, DisconnectSlc) {
ResetStubData();
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sock));
- handle = hfp_slc_create(sock[0], 0, slc_initialized_cb,
+ handle = hfp_slc_create(sock[0], 0, device, slc_initialized_cb,
slc_disconnected_cb);
/* Close socket right away to make read() get negative err code, and
* fake the errno to ECONNRESET. */
@@ -135,6 +151,12 @@ int cras_system_add_select_fd(int fd,
void cras_system_rm_select_fd(int fd) {
}
+void cras_bt_device_update_hardware_volume(struct cras_bt_device *device,
+ int volume)
+{
+ cras_bt_device_update_hardware_volume_called++;
+}
+
/* To return fake errno */
int *__errno_location() {
return &fake_errno;
diff --git a/cras/src/tests/iodev_list_unittest.cc b/cras/src/tests/iodev_list_unittest.cc
index c24cf094..35b515d4 100644
--- a/cras/src/tests/iodev_list_unittest.cc
+++ b/cras/src/tests/iodev_list_unittest.cc
@@ -2,13 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <algorithm>
#include <stdio.h>
#include <gtest/gtest.h>
+#include <map>
extern "C" {
#include "audio_thread.h"
#include "cras_iodev.h"
#include "cras_iodev_list.h"
+#include "cras_observer_ops.h"
+#include "cras_ramp.h"
#include "cras_rstream.h"
#include "cras_system_state.h"
#include "cras_tm.h"
@@ -22,32 +26,11 @@ struct cras_server_state server_state_stub;
struct cras_server_state *server_state_update_begin_return;
/* Data for stubs. */
-static cras_alert_cb volume_changed_cb;
-static void* volume_changed_arg;
-static unsigned int register_volume_changed_cb_called;
-static unsigned int remove_volume_changed_cb_called;
-static cras_alert_cb mute_changed_cb;
-static cras_alert_cb suspend_cb;
-static void* mute_changed_arg;
-static unsigned int register_mute_changed_cb_called;
-static unsigned int remove_mute_changed_cb_called;
-static unsigned int register_suspend_cb_called;
-static unsigned int remove_suspend_cb_called;
+static struct cras_observer_ops *observer_ops;
static unsigned int cras_system_get_suspended_val;
-static cras_alert_cb capture_gain_changed_cb;
-static void* capture_gain_changed_arg;
-static unsigned int register_capture_gain_changed_cb_called;
-static unsigned int remove_capture_gain_changed_cb_called;
-static cras_alert_cb capture_mute_changed_cb;
-static void* capture_mute_changed_arg;
-static unsigned int register_capture_mute_changed_cb_called;
-static unsigned int remove_capture_mute_changed_cb_called;
static int add_stream_called;
static int rm_stream_called;
static unsigned int set_node_attr_called;
-static int cras_alert_create_called;
-static int cras_alert_destroy_called;
-static int cras_alert_pending_called;
static cras_iodev *audio_thread_remove_streams_active_dev;
static cras_iodev *audio_thread_set_active_dev_val;
static int audio_thread_set_active_dev_called;
@@ -55,39 +38,57 @@ static cras_iodev *audio_thread_add_open_dev_dev;
static int audio_thread_add_open_dev_called;
static int audio_thread_rm_open_dev_called;
static struct audio_thread thread;
-static int node_left_right_swapped_cb_called;
static struct cras_iodev loopback_input;
static int cras_iodev_close_called;
static struct cras_iodev *cras_iodev_close_dev;
static struct cras_iodev dummy_empty_iodev[2];
static stream_callback *stream_add_cb;
static stream_callback *stream_rm_cb;
-static int iodev_is_open;
-static int empty_iodev_is_open[CRAS_NUM_DIRECTIONS];
static struct cras_rstream *stream_list_get_ret;
static int audio_thread_drain_stream_return;
static int audio_thread_drain_stream_called;
+static int cras_tm_create_timer_called;
+static int cras_tm_cancel_timer_called;
static void (*cras_tm_timer_cb)(struct cras_timer *t, void *data);
+static void *cras_tm_timer_cb_data;
static struct timespec clock_gettime_retspec;
static struct cras_iodev *device_enabled_dev;
+static int device_enabled_count;
static struct cras_iodev *device_disabled_dev;
+static int device_disabled_count;
static void *device_enabled_cb_data;
static struct cras_rstream *audio_thread_add_stream_stream;
static struct cras_iodev *audio_thread_add_stream_dev;
static int audio_thread_add_stream_called;
-
-/* Callback in iodev_list. */
-void node_left_right_swapped_cb(cras_node_id_t, int)
+static unsigned update_active_node_called;
+static struct cras_iodev *update_active_node_iodev_val[5];
+static unsigned update_active_node_node_idx_val[5];
+static unsigned update_active_node_dev_enabled_val[5];
+static size_t cras_observer_add_called;
+static size_t cras_observer_remove_called;
+static size_t cras_observer_notify_nodes_called;
+static size_t cras_observer_notify_active_node_called;
+static size_t cras_observer_notify_output_node_volume_called;
+static size_t cras_observer_notify_node_left_right_swapped_called;
+static size_t cras_observer_notify_input_node_gain_called;
+static int cras_iodev_open_called;
+static int cras_iodev_open_ret[8];
+static int set_mute_called;
+static std::vector<struct cras_iodev*> set_mute_dev_vector;
+static struct cras_iodev *audio_thread_dev_start_ramp_dev;
+static int audio_thread_dev_start_ramp_called;
+static enum CRAS_IODEV_RAMP_REQUEST audio_thread_dev_start_ramp_req ;
+static std::map<const struct cras_iodev*, enum CRAS_IODEV_STATE> cras_iodev_state_ret;
+static int cras_iodev_is_zero_volume_ret;
+
+void dummy_update_active_node(struct cras_iodev *iodev,
+ unsigned node_idx,
+ unsigned dev_enabled) {
+}
+
+int device_in_vector(std::vector<struct cras_iodev*> v, struct cras_iodev *dev)
{
- node_left_right_swapped_cb_called++;
-}
-
-/* For iodev is_open. */
-int cras_iodev_is_open_stub(const struct cras_iodev *dev) {
- enum CRAS_STREAM_DIRECTION dir = dev->direction;
- if (dev == &dummy_empty_iodev[dir])
- return empty_iodev_is_open[dir];
- return iodev_is_open;
+ return std::find(v.begin(), v.end(), dev) != v.end();
}
class IoDevTestSuite : public testing::Test {
@@ -99,6 +100,8 @@ class IoDevTestSuite : public testing::Test {
stream_list_get_ret = 0;
audio_thread_drain_stream_return = 0;
audio_thread_drain_stream_called = 0;
+ cras_tm_create_timer_called = 0;
+ cras_tm_cancel_timer_called = 0;
sample_rates_[0] = 44100;
sample_rates_[1] = 48000;
@@ -116,10 +119,8 @@ class IoDevTestSuite : public testing::Test {
memset(&node3, 0, sizeof(node3));
d1_.set_volume = NULL;
- d1_.set_mute = NULL;
d1_.set_capture_gain = NULL;
d1_.set_capture_mute = NULL;
- d1_.is_open = is_open;
d1_.update_supported_formats = NULL;
d1_.update_active_node = update_active_node;
d1_.format = NULL;
@@ -131,10 +132,8 @@ class IoDevTestSuite : public testing::Test {
d1_.supported_rates = sample_rates_;
d1_.supported_channel_counts = channel_counts_;
d2_.set_volume = NULL;
- d2_.set_mute = NULL;
d2_.set_capture_gain = NULL;
d2_.set_capture_mute = NULL;
- d2_.is_open = is_open;
d2_.update_supported_formats = NULL;
d2_.update_active_node = update_active_node;
d2_.format = NULL;
@@ -146,10 +145,8 @@ class IoDevTestSuite : public testing::Test {
d2_.supported_rates = sample_rates_;
d2_.supported_channel_counts = channel_counts_;
d3_.set_volume = NULL;
- d3_.set_mute = NULL;
d3_.set_capture_gain = NULL;
d3_.set_capture_mute = NULL;
- d3_.is_open = is_open;
d3_.update_supported_formats = NULL;
d3_.update_active_node = update_active_node;
d3_.format = NULL;
@@ -162,10 +159,8 @@ class IoDevTestSuite : public testing::Test {
d3_.supported_channel_counts = channel_counts_;
loopback_input.set_volume = NULL;
- loopback_input.set_mute = NULL;
loopback_input.set_capture_gain = NULL;
loopback_input.set_capture_mute = NULL;
- loopback_input.is_open = is_open;
loopback_input.update_supported_formats = NULL;
loopback_input.update_active_node = update_active_node;
loopback_input.format = NULL;
@@ -180,38 +175,36 @@ class IoDevTestSuite : public testing::Test {
server_state_update_begin_return = &server_state_stub;
/* Reset stub data. */
- register_volume_changed_cb_called = 0;
- remove_volume_changed_cb_called = 0;
- register_capture_gain_changed_cb_called = 0;
- remove_capture_gain_changed_cb_called = 0;
- register_mute_changed_cb_called = 0;
- remove_mute_changed_cb_called = 0;
- register_suspend_cb_called = 0;
- remove_suspend_cb_called = 0;
- register_capture_mute_changed_cb_called = 0;
- remove_capture_mute_changed_cb_called = 0;
add_stream_called = 0;
rm_stream_called = 0;
set_node_attr_called = 0;
- cras_alert_create_called = 0;
- cras_alert_destroy_called = 0;
- cras_alert_pending_called = 0;
- is_open_ = 0;
audio_thread_rm_open_dev_called = 0;
audio_thread_add_open_dev_called = 0;
audio_thread_set_active_dev_called = 0;
- node_left_right_swapped_cb_called = 0;
audio_thread_add_stream_called = 0;
+ update_active_node_called = 0;
+ cras_observer_add_called = 0;
+ cras_observer_remove_called = 0;
+ cras_observer_notify_nodes_called = 0;
+ cras_observer_notify_active_node_called = 0;
+ cras_observer_notify_output_node_volume_called = 0;
+ cras_observer_notify_node_left_right_swapped_called = 0;
+ cras_observer_notify_input_node_gain_called = 0;
+ cras_iodev_open_called = 0;
+ memset(cras_iodev_open_ret, 0, sizeof(cras_iodev_open_ret));
+ set_mute_called = 0;
+ set_mute_dev_vector.clear();
+ audio_thread_dev_start_ramp_dev = NULL;
+ audio_thread_dev_start_ramp_called = 0;
+ audio_thread_dev_start_ramp_req =
+ CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK;
+ cras_iodev_is_zero_volume_ret = 0;
}
static void set_volume_1(struct cras_iodev* iodev) {
set_volume_1_called_++;
}
- static void set_mute_1(struct cras_iodev* iodev) {
- set_mute_1_called_++;
- }
-
static void set_capture_gain_1(struct cras_iodev* iodev) {
set_capture_gain_1_called_++;
}
@@ -221,11 +214,12 @@ class IoDevTestSuite : public testing::Test {
}
static void update_active_node(struct cras_iodev *iodev,
- unsigned node_idx) {
- }
-
- static int is_open(const cras_iodev* iodev) {
- return is_open_;
+ unsigned node_idx,
+ unsigned dev_enabled) {
+ int i = update_active_node_called++ % 5;
+ update_active_node_iodev_val[i] = iodev;
+ update_active_node_node_idx_val[i] = node_idx;
+ update_active_node_dev_enabled_val[i] = dev_enabled;
}
struct cras_iodev d1_;
@@ -234,33 +228,21 @@ class IoDevTestSuite : public testing::Test {
size_t sample_rates_[3];
size_t channel_counts_[2];
static int set_volume_1_called_;
- static int set_mute_1_called_;
static int set_capture_gain_1_called_;
static int set_capture_mute_1_called_;
- static int is_open_;
struct cras_ionode node1, node2, node3;
};
int IoDevTestSuite::set_volume_1_called_;
-int IoDevTestSuite::set_mute_1_called_;
int IoDevTestSuite::set_capture_gain_1_called_;
int IoDevTestSuite::set_capture_mute_1_called_;
-int IoDevTestSuite::is_open_;
-// Check that Init registers a volume changed callback. */
+// Check that Init registers observer client. */
TEST_F(IoDevTestSuite, InitSetup) {
cras_iodev_list_init();
- EXPECT_EQ(1, register_volume_changed_cb_called);
- EXPECT_EQ(1, register_mute_changed_cb_called);
- EXPECT_EQ(1, register_suspend_cb_called);
- EXPECT_EQ(1, register_capture_gain_changed_cb_called);
- EXPECT_EQ(1, register_capture_mute_changed_cb_called);
+ EXPECT_EQ(1, cras_observer_add_called);
cras_iodev_list_deinit();
- EXPECT_EQ(1, remove_volume_changed_cb_called);
- EXPECT_EQ(1, remove_mute_changed_cb_called);
- EXPECT_EQ(1, remove_suspend_cb_called);
- EXPECT_EQ(1, remove_capture_gain_changed_cb_called);
- EXPECT_EQ(1, remove_capture_mute_changed_cb_called);
+ EXPECT_EQ(1, cras_observer_remove_called);
}
/* Check that the suspend alert from cras_system will trigger suspend
@@ -277,11 +259,9 @@ TEST_F(IoDevTestSuite, SetSuspendResume) {
cras_iodev_list_init();
d1_.direction = CRAS_STREAM_OUTPUT;
- d1_.is_open = cras_iodev_is_open_stub;
rc = cras_iodev_list_add_output(&d1_);
ASSERT_EQ(0, rc);
- iodev_is_open = 0;
audio_thread_add_open_dev_called = 0;
cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
cras_make_node_id(d1_.info.idx, 1));
@@ -289,7 +269,6 @@ TEST_F(IoDevTestSuite, SetSuspendResume) {
stream_add_cb(&rstream);
EXPECT_EQ(1, audio_thread_add_stream_called);
EXPECT_EQ(1, audio_thread_add_open_dev_called);
- iodev_is_open = 1;
DL_APPEND(stream_list, &rstream2);
stream_add_cb(&rstream2);
@@ -297,9 +276,8 @@ TEST_F(IoDevTestSuite, SetSuspendResume) {
cras_system_get_suspended_val = 1;
audio_thread_rm_open_dev_called = 0;
- suspend_cb(NULL);
+ observer_ops->suspend_changed(NULL, 1);
EXPECT_EQ(1, audio_thread_rm_open_dev_called);
- iodev_is_open = 0;
/* Test disable/enable dev won't cause add_stream to audio_thread. */
audio_thread_add_stream_called = 0;
@@ -322,54 +300,357 @@ TEST_F(IoDevTestSuite, SetSuspendResume) {
audio_thread_add_stream_called = 0;
cras_system_get_suspended_val = 0;
stream_list_get_ret = stream_list;
- suspend_cb(NULL);
+ observer_ops->suspend_changed(NULL, 0);
EXPECT_EQ(1, audio_thread_add_open_dev_called);
EXPECT_EQ(2, audio_thread_add_stream_called);
EXPECT_EQ(&rstream3, audio_thread_add_stream_stream);
- iodev_is_open = 1;
cras_iodev_list_deinit();
+ EXPECT_EQ(3, cras_observer_notify_active_node_called);
}
-TEST_F(IoDevTestSuite, SelectNode) {
- struct cras_rstream rstream, rstream2, rstream3;
+TEST_F(IoDevTestSuite, InitDevFailShouldEnableFallback) {
+ int rc;
+ struct cras_rstream rstream;
+ struct cras_rstream *stream_list = NULL;
+
+ memset(&rstream, 0, sizeof(rstream));
+ cras_iodev_list_init();
+
+ d1_.direction = CRAS_STREAM_OUTPUT;
+ rc = cras_iodev_list_add_output(&d1_);
+ ASSERT_EQ(0, rc);
+
+ cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
+ cras_make_node_id(d1_.info.idx, 0));
+
+ cras_iodev_open_ret[0] = -5;
+ cras_iodev_open_ret[1] = 0;
+
+ DL_APPEND(stream_list, &rstream);
+ stream_list_get_ret = stream_list;
+ stream_add_cb(&rstream);
+ /* open dev called twice, one for fallback device. */
+ EXPECT_EQ(2, cras_iodev_open_called);
+ EXPECT_EQ(1, audio_thread_add_stream_called);
+}
+
+TEST_F(IoDevTestSuite, SelectNodeOpenFailShouldScheduleRetry) {
+ struct cras_rstream rstream;
struct cras_rstream *stream_list = NULL;
int rc;
memset(&rstream, 0, sizeof(rstream));
+ cras_iodev_list_init();
+
+ d1_.direction = CRAS_STREAM_OUTPUT;
+ rc = cras_iodev_list_add_output(&d1_);
+ ASSERT_EQ(0, rc);
+
+ d2_.direction = CRAS_STREAM_OUTPUT;
+ rc = cras_iodev_list_add_output(&d2_);
+ ASSERT_EQ(0, rc);
+
+ cras_iodev_list_select_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);
+
+ /* Select node triggers: fallback open, d1 close, d2 open, fallback close. */
+ cras_iodev_close_called = 0;
+ cras_iodev_open_called = 0;
+ cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
+ cras_make_node_id(d2_.info.idx, 1));
+ EXPECT_EQ(2, cras_iodev_close_called);
+ EXPECT_EQ(2, cras_iodev_open_called);
+ EXPECT_EQ(0, cras_tm_create_timer_called);
+ EXPECT_EQ(0, cras_tm_cancel_timer_called);
+
+ /* Test that if select to d1 and open d1 fail, fallback doesn't close. */
+ cras_iodev_open_called = 0;
+ cras_iodev_open_ret[0] = 0;
+ cras_iodev_open_ret[1] = -5;
+ cras_iodev_open_ret[2] = 0;
+ cras_tm_timer_cb = NULL;
+ cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
+ cras_make_node_id(d1_.info.idx, 1));
+ EXPECT_EQ(3, cras_iodev_close_called);
+ EXPECT_EQ(&d2_, cras_iodev_close_dev);
+ EXPECT_EQ(2, cras_iodev_open_called);
+ EXPECT_EQ(0, cras_tm_cancel_timer_called);
+
+ /* Assert a timer is scheduled to retry open. */
+ EXPECT_NE((void *)NULL, cras_tm_timer_cb);
+ EXPECT_EQ(1, cras_tm_create_timer_called);
+
+ audio_thread_add_stream_called = 0;
+ cras_tm_timer_cb(NULL, cras_tm_timer_cb_data);
+ EXPECT_EQ(3, cras_iodev_open_called);
+ EXPECT_EQ(1, audio_thread_add_stream_called);
+
+ /* Retry open success will close fallback dev. */
+ EXPECT_EQ(4, cras_iodev_close_called);
+ EXPECT_EQ(0, cras_tm_cancel_timer_called);
+
+ /* Select to d2 and fake an open failure. */
+ cras_iodev_close_called = 0;
+ cras_iodev_open_called = 0;
+ cras_iodev_open_ret[0] = 0;
+ cras_iodev_open_ret[1] = -5;
+ cras_iodev_open_ret[2] = 0;
+ cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
+ cras_make_node_id(d2_.info.idx, 1));
+ EXPECT_EQ(1, cras_iodev_close_called);
+ EXPECT_EQ(&d1_, cras_iodev_close_dev);
+ EXPECT_EQ(2, cras_tm_create_timer_called);
+ EXPECT_NE((void *)NULL, cras_tm_timer_cb);
+
+ /* Select to another iodev should cancel the timer. */
+ memset(cras_iodev_open_ret, 0, sizeof(cras_iodev_open_ret));
+ cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
+ cras_make_node_id(d2_.info.idx, 1));
+ EXPECT_EQ(1, cras_tm_cancel_timer_called);
+}
+
+TEST_F(IoDevTestSuite, InitDevFailShouldScheduleRetry) {
+ int rc;
+ struct cras_rstream rstream;
+ struct cras_rstream *stream_list = NULL;
+
+ memset(&rstream, 0, sizeof(rstream));
+ cras_iodev_list_init();
+
+ d1_.direction = CRAS_STREAM_OUTPUT;
+ rc = cras_iodev_list_add_output(&d1_);
+ ASSERT_EQ(0, rc);
+
+ cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
+ cras_make_node_id(d1_.info.idx, 0));
+
+ cras_iodev_open_ret[0] = -5;
+ cras_iodev_open_ret[1] = 0;
+ cras_tm_timer_cb = NULL;
+ DL_APPEND(stream_list, &rstream);
+ stream_list_get_ret = stream_list;
+ stream_add_cb(&rstream);
+ /* open dev called twice, one for fallback device. */
+ EXPECT_EQ(2, cras_iodev_open_called);
+ EXPECT_EQ(1, audio_thread_add_stream_called);
+
+ EXPECT_NE((void *)NULL, cras_tm_timer_cb);
+ EXPECT_EQ(1, cras_tm_create_timer_called);
+
+ /* If retry still fail, won't schedule more retry. */
+ cras_iodev_open_ret[2] = -5;
+ cras_tm_timer_cb(NULL, cras_tm_timer_cb_data);
+ EXPECT_EQ(1, cras_tm_create_timer_called);
+ EXPECT_EQ(1, audio_thread_add_stream_called);
+
+ cras_tm_timer_cb = NULL;
+ cras_iodev_open_ret[3] = -5;
+ stream_add_cb(&rstream);
+ EXPECT_NE((void *)NULL, cras_tm_timer_cb);
+ EXPECT_EQ(2, cras_tm_create_timer_called);
+
+ cras_iodev_list_rm_output(&d1_);
+ EXPECT_EQ(1, cras_tm_cancel_timer_called);
+}
+
+static void device_enabled_cb(struct cras_iodev *dev, int enabled,
+ void *cb_data)
+{
+ if (enabled) {
+ device_enabled_dev = dev;
+ device_enabled_count++;
+ } else {
+ device_disabled_dev = dev;
+ device_disabled_count++;
+ }
+ device_enabled_cb_data = cb_data;
+}
+
+TEST_F(IoDevTestSuite, SelectNode) {
+ struct cras_rstream rstream, rstream2;
+ int rc;
+
+ memset(&rstream, 0, sizeof(rstream));
memset(&rstream2, 0, sizeof(rstream2));
- memset(&rstream3, 0, sizeof(rstream3));
cras_iodev_list_init();
d1_.direction = CRAS_STREAM_OUTPUT;
- d1_.is_open = cras_iodev_is_open_stub;
+ node1.idx = 1;
rc = cras_iodev_list_add_output(&d1_);
ASSERT_EQ(0, rc);
d2_.direction = CRAS_STREAM_OUTPUT;
- d2_.is_open = cras_iodev_is_open_stub;
+ node2.idx = 2;
rc = cras_iodev_list_add_output(&d2_);
ASSERT_EQ(0, rc);
- iodev_is_open = 0;
audio_thread_add_open_dev_called = 0;
+ audio_thread_rm_open_dev_called = 0;
+
+ device_enabled_count = 0;
+ device_disabled_count = 0;
+
+ EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(
+ device_enabled_cb, (void *)0xABCD));
+
cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
cras_make_node_id(d1_.info.idx, 1));
- DL_APPEND(stream_list, &rstream);
+
+ EXPECT_EQ(1, device_enabled_count);
+ EXPECT_EQ(1, cras_observer_notify_active_node_called);
+ EXPECT_EQ(&d1_, cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT));
+
+ // There should be a disable device call for the fallback device.
+ EXPECT_EQ(1, audio_thread_rm_open_dev_called);
+ EXPECT_EQ(1, device_disabled_count);
+ EXPECT_NE(&d1_, device_disabled_dev);
+
+ DL_APPEND(stream_list_get_ret, &rstream);
stream_add_cb(&rstream);
+
EXPECT_EQ(1, audio_thread_add_stream_called);
EXPECT_EQ(1, audio_thread_add_open_dev_called);
- iodev_is_open = 1;
- DL_APPEND(stream_list, &rstream2);
+ DL_APPEND(stream_list_get_ret, &rstream2);
stream_add_cb(&rstream2);
+
EXPECT_EQ(2, audio_thread_add_stream_called);
+ EXPECT_EQ(1, audio_thread_add_open_dev_called);
+
+ cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
+ cras_make_node_id(d2_.info.idx, 2));
+
+ // Additional enabled devices: fallback device, d2_.
+ EXPECT_EQ(3, device_enabled_count);
+ // Additional disabled devices: d1_, fallback device.
+ EXPECT_EQ(3, device_disabled_count);
+ EXPECT_EQ(3, audio_thread_rm_open_dev_called);
+ EXPECT_EQ(2, cras_observer_notify_active_node_called);
+ EXPECT_EQ(&d2_, cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT));
+
+ // For each stream, the stream is added for fallback device and d2_.
+ EXPECT_EQ(6, audio_thread_add_stream_called);
+
+ EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(NULL, NULL));
+}
+
+TEST_F(IoDevTestSuite, SelectPreviouslyEnabledNode) {
+ struct cras_rstream rstream;
+ int rc;
+
+ memset(&rstream, 0, sizeof(rstream));
+
+ cras_iodev_list_init();
+
+ d1_.direction = CRAS_STREAM_OUTPUT;
+ node1.idx = 1;
+ rc = cras_iodev_list_add_output(&d1_);
+ ASSERT_EQ(0, rc);
+
+ d2_.direction = CRAS_STREAM_OUTPUT;
+ node2.idx = 2;
+ rc = cras_iodev_list_add_output(&d2_);
+ ASSERT_EQ(0, rc);
+
+ audio_thread_add_open_dev_called = 0;
+ audio_thread_rm_open_dev_called = 0;
+ device_enabled_count = 0;
+ device_disabled_count = 0;
+
+ EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(
+ device_enabled_cb, (void *)0xABCD));
+
+ // Add an active node.
+ cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
+ cras_make_node_id(d1_.info.idx, 1));
+
+ EXPECT_EQ(1, device_enabled_count);
+ EXPECT_EQ(1, cras_observer_notify_active_node_called);
+ EXPECT_EQ(&d1_, cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT));
+
+ // There should be a disable device call for the fallback device.
+ EXPECT_EQ(1, device_disabled_count);
+ EXPECT_NE(&d1_, device_disabled_dev);
+ EXPECT_NE(&d2_, device_disabled_dev);
+
+ DL_APPEND(stream_list_get_ret, &rstream);
+ stream_add_cb(&rstream);
+
+ EXPECT_EQ(1, audio_thread_add_open_dev_called);
+ EXPECT_EQ(1, audio_thread_add_stream_called);
+
+ // Add a second active node.
+ cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
+ cras_make_node_id(d2_.info.idx, 2));
+
+ EXPECT_EQ(2, device_enabled_count);
+ EXPECT_EQ(1, device_disabled_count);
+ EXPECT_EQ(2, cras_observer_notify_active_node_called);
+ EXPECT_EQ(&d1_, cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT));
+
+ EXPECT_EQ(2, audio_thread_add_open_dev_called);
+ EXPECT_EQ(2, audio_thread_add_stream_called);
+ EXPECT_EQ(0, audio_thread_rm_open_dev_called);
+
+ // Select the second added active node - the initially added node should get
+ // disabled.
+ cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
+ cras_make_node_id(d2_.info.idx, 2));
+
+ EXPECT_EQ(2, device_enabled_count);
+ EXPECT_EQ(2, device_disabled_count);
+ EXPECT_EQ(3, cras_observer_notify_active_node_called);
+
+ EXPECT_EQ(&d2_, cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT));
+ EXPECT_EQ(&d1_, device_disabled_dev);
+
+ EXPECT_EQ(2, audio_thread_add_stream_called);
+ EXPECT_EQ(2, audio_thread_add_open_dev_called);
+ EXPECT_EQ(1, audio_thread_rm_open_dev_called);
+
+ EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(NULL, NULL));
+}
+
+TEST_F(IoDevTestSuite, UpdateActiveNode) {
+ int rc;
+
+ cras_iodev_list_init();
+
+ d1_.direction = CRAS_STREAM_OUTPUT;
+ rc = cras_iodev_list_add_output(&d1_);
+ ASSERT_EQ(0, rc);
+
+ d2_.direction = CRAS_STREAM_OUTPUT;
+ rc = cras_iodev_list_add_output(&d2_);
+ ASSERT_EQ(0, rc);
- stream_list_get_ret = stream_list;
cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
cras_make_node_id(d2_.info.idx, 1));
- EXPECT_EQ(4, audio_thread_add_stream_called);
+
+ EXPECT_EQ(1, update_active_node_called);
+ EXPECT_EQ(&d2_, update_active_node_iodev_val[0]);
+ EXPECT_EQ(1, update_active_node_node_idx_val[0]);
+ EXPECT_EQ(1, update_active_node_dev_enabled_val[0]);
+
+ /* Fake the active node idx on d2_, and later assert this node is
+ * called for update_active_node when d2_ disabled. */
+ d2_.active_node->idx = 2;
+ cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
+ cras_make_node_id(d1_.info.idx, 0));
+
+ EXPECT_EQ(3, update_active_node_called);
+ EXPECT_EQ(&d2_, update_active_node_iodev_val[1]);
+ EXPECT_EQ(&d1_, update_active_node_iodev_val[2]);
+ EXPECT_EQ(2, update_active_node_node_idx_val[1]);
+ EXPECT_EQ(0, update_active_node_node_idx_val[2]);
+ EXPECT_EQ(0, update_active_node_dev_enabled_val[1]);
+ EXPECT_EQ(1, update_active_node_dev_enabled_val[2]);
+ EXPECT_EQ(2, cras_observer_notify_active_node_called);
}
TEST_F(IoDevTestSuite, SelectNonExistingNode) {
@@ -377,7 +658,6 @@ TEST_F(IoDevTestSuite, SelectNonExistingNode) {
cras_iodev_list_init();
d1_.direction = CRAS_STREAM_OUTPUT;
- d1_.is_open = cras_iodev_is_open_stub;
rc = cras_iodev_list_add_output(&d1_);
ASSERT_EQ(0, rc);
@@ -389,6 +669,7 @@ TEST_F(IoDevTestSuite, SelectNonExistingNode) {
cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
cras_make_node_id(2, 1));
EXPECT_EQ(0, d1_.is_enabled);
+ EXPECT_EQ(2, cras_observer_notify_active_node_called);
}
// Devices with the wrong direction should be rejected.
@@ -436,20 +717,181 @@ TEST_F(IoDevTestSuite, AddRemoveOutput) {
rc = cras_iodev_list_get_outputs(&dev_info);
EXPECT_EQ(0, rc);
free(dev_info);
+ EXPECT_EQ(0, cras_observer_notify_active_node_called);
}
-static void device_enabled_cb(struct cras_iodev *dev, int enabled,
- void *cb_data)
-{
- if (enabled)
- device_enabled_dev = dev;
- else
- device_disabled_dev = dev;
- device_enabled_cb_data = cb_data;
+// Test output_mute_changed callback.
+TEST_F(IoDevTestSuite, OutputMuteChangedToMute) {
+ cras_iodev_list_init();
+
+ // d1_ and d3_ have ramp while d2_ does not have ramp.
+ d1_.ramp = reinterpret_cast<cras_ramp*>(0x123);
+ d2_.ramp = NULL;
+ d3_.ramp = reinterpret_cast<cras_ramp*>(0x124);
+
+ cras_iodev_list_add_output(&d1_);
+ cras_iodev_list_add_output(&d2_);
+ cras_iodev_list_add_output(&d3_);
+
+ // d1_ and d2_ are enabled.
+ cras_iodev_list_enable_dev(&d1_);
+ cras_iodev_list_enable_dev(&d2_);
+
+ // Assume d1 and d2 devices are in normal run.
+ cras_iodev_state_ret[&d1_] = CRAS_IODEV_STATE_NORMAL_RUN;
+ cras_iodev_state_ret[&d2_] = CRAS_IODEV_STATE_NORMAL_RUN;
+ cras_iodev_state_ret[&d3_] = CRAS_IODEV_STATE_CLOSE;
+
+ // Execute the callback.
+ observer_ops->output_mute_changed(NULL, 0, 1, 0);
+
+ // d1_ should set mute state through audio_thread_dev_start_ramp.
+ EXPECT_EQ(&d1_, audio_thread_dev_start_ramp_dev);
+ EXPECT_EQ(1, audio_thread_dev_start_ramp_called);
+ EXPECT_EQ(CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE, audio_thread_dev_start_ramp_req);
+
+ // d2_ should set mute state right away.
+ // d3_ should set mute state right away without calling ramp
+ // because it is not enabled.
+ EXPECT_EQ(2, set_mute_called);
+ EXPECT_EQ(2, set_mute_dev_vector.size());
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d2_));
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d3_));
+
+ // Assume d1_ should mute for volume.
+ // It should not use ramp.
+ cras_iodev_is_zero_volume_ret = 1;
+
+ // Clear stub data of interest.
+ audio_thread_dev_start_ramp_dev = NULL;
+ audio_thread_dev_start_ramp_called = 0;
+ set_mute_called = 0;
+ set_mute_dev_vector.clear();
+
+ // Execute the callback.
+ observer_ops->output_mute_changed(NULL, 0, 1, 0);
+
+ // Verify three devices all set mute state right away.
+ EXPECT_EQ(NULL, audio_thread_dev_start_ramp_dev);
+ EXPECT_EQ(0, audio_thread_dev_start_ramp_called);
+ EXPECT_EQ(3, set_mute_called);
+ EXPECT_EQ(3, set_mute_dev_vector.size());
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d1_));
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d2_));
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d3_));
+
+ // Assume d1_ is changed to no_stream run state
+ // It should not use ramp.
+ cras_iodev_state_ret[&d1_] = CRAS_IODEV_STATE_NO_STREAM_RUN;
+
+ // Clear stub data of interest.
+ audio_thread_dev_start_ramp_dev = NULL;
+ audio_thread_dev_start_ramp_called = 0;
+ set_mute_called = 0;
+ set_mute_dev_vector.clear();
+
+ // Execute the callback.
+ observer_ops->output_mute_changed(NULL, 0, 1, 0);
+
+ // Verify three devices all set mute state right away.
+ EXPECT_EQ(NULL, audio_thread_dev_start_ramp_dev);
+ EXPECT_EQ(0, audio_thread_dev_start_ramp_called);
+ EXPECT_EQ(3, set_mute_called);
+ EXPECT_EQ(3, set_mute_dev_vector.size());
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d1_));
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d2_));
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d3_));
+}
+
+// Test output_mute_changed callback.
+TEST_F(IoDevTestSuite, OutputMuteChangedToUnmute) {
+ cras_iodev_list_init();
+
+ // d1_ and d3_ have ramp while d2_ does not have ramp.
+ d1_.ramp = reinterpret_cast<cras_ramp*>(0x123);
+ d2_.ramp = NULL;
+ d3_.ramp = reinterpret_cast<cras_ramp*>(0x124);
+
+ cras_iodev_list_add_output(&d1_);
+ cras_iodev_list_add_output(&d2_);
+ cras_iodev_list_add_output(&d3_);
+
+ // d1_ and d2_ are enabled.
+ cras_iodev_list_enable_dev(&d1_);
+ cras_iodev_list_enable_dev(&d2_);
+
+ // Assume d1 and d2 devices are in normal run.
+ cras_iodev_state_ret[&d1_] = CRAS_IODEV_STATE_NORMAL_RUN;
+ cras_iodev_state_ret[&d2_] = CRAS_IODEV_STATE_NORMAL_RUN;
+ cras_iodev_state_ret[&d3_] = CRAS_IODEV_STATE_CLOSE;
+
+ // Execute the callback.
+ observer_ops->output_mute_changed(NULL, 0, 0, 0);
+
+ // d1_ should set mute state through audio_thread_dev_start_ramp.
+ EXPECT_EQ(&d1_, audio_thread_dev_start_ramp_dev);
+ EXPECT_EQ(1, audio_thread_dev_start_ramp_called);
+ EXPECT_EQ(CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE,
+ audio_thread_dev_start_ramp_req);
+
+ // d2_ should set mute state right away.
+ // d3_ should set mute state right away without calling ramp
+ // because it is not enabled.
+ EXPECT_EQ(2, set_mute_called);
+ EXPECT_EQ(2, set_mute_dev_vector.size());
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d2_));
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d3_));
+
+ // Assume d1_ should mute for volume.
+ // It should not use ramp.
+ cras_iodev_is_zero_volume_ret = 1;
+
+ // Clear stub data of interest.
+ audio_thread_dev_start_ramp_dev = NULL;
+ audio_thread_dev_start_ramp_called = 0;
+ set_mute_called = 0;
+ set_mute_dev_vector.clear();
+
+ // Execute the callback.
+ observer_ops->output_mute_changed(NULL, 0, 1, 0);
+
+ // Verify three devices all set mute state right away.
+ EXPECT_EQ(NULL, audio_thread_dev_start_ramp_dev);
+ EXPECT_EQ(0, audio_thread_dev_start_ramp_called);
+ EXPECT_EQ(3, set_mute_called);
+ EXPECT_EQ(3, set_mute_dev_vector.size());
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d1_));
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d2_));
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d3_));
+
+ // Assume d1_ is changed to no_stream run state
+ // It should not use ramp.
+ cras_iodev_state_ret[&d1_] = CRAS_IODEV_STATE_NO_STREAM_RUN;
+
+ // Clear stub data of interest.
+ audio_thread_dev_start_ramp_dev = NULL;
+ audio_thread_dev_start_ramp_called = 0;
+ set_mute_called = 0;
+ set_mute_dev_vector.clear();
+
+ // Execute the callback.
+ observer_ops->output_mute_changed(NULL, 0, 1, 0);
+
+ // Verify three devices all set mute state right away.
+ EXPECT_EQ(NULL, audio_thread_dev_start_ramp_dev);
+ EXPECT_EQ(0, audio_thread_dev_start_ramp_called);
+ EXPECT_EQ(3, set_mute_called);
+ EXPECT_EQ(3, set_mute_dev_vector.size());
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d1_));
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d2_));
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d3_));
}
// Test enable/disable an iodev.
TEST_F(IoDevTestSuite, EnableDisableDevice) {
+ device_enabled_count = 0;
+ device_disabled_count = 0;
+
EXPECT_EQ(0, cras_iodev_list_add_output(&d1_));
EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(
@@ -459,15 +901,20 @@ TEST_F(IoDevTestSuite, EnableDisableDevice) {
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(&d1_, cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT));
// Disable a device.
cras_iodev_list_disable_dev(&d1_);
EXPECT_EQ(&d1_, device_disabled_dev);
+ EXPECT_EQ(1, device_disabled_count);
EXPECT_EQ((void *)0xABCD, device_enabled_cb_data);
EXPECT_EQ(-EEXIST, cras_iodev_list_set_device_enabled_callback(
device_enabled_cb, (void *)0xABCD));
+ EXPECT_EQ(2, cras_observer_notify_active_node_called);
+
+ EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(NULL, NULL));
}
// Test adding/removing an input dev to the list.
@@ -603,18 +1050,14 @@ TEST_F(IoDevTestSuite, RemoveLastInput) {
// Test nodes changed notification is sent.
TEST_F(IoDevTestSuite, NodesChangedNotification) {
- EXPECT_EQ(0, cras_alert_create_called);
cras_iodev_list_init();
- /* One for nodes changed and one for active node changed */
- EXPECT_EQ(2, cras_alert_create_called);
+ EXPECT_EQ(1, cras_observer_add_called);
- EXPECT_EQ(0, cras_alert_pending_called);
cras_iodev_list_notify_nodes_changed();
- EXPECT_EQ(1, cras_alert_pending_called);
+ EXPECT_EQ(1, cras_observer_notify_nodes_called);
- EXPECT_EQ(0, cras_alert_destroy_called);
cras_iodev_list_deinit();
- EXPECT_EQ(2, cras_alert_destroy_called);
+ EXPECT_EQ(1, cras_observer_remove_called);
}
// Test callback function for left right swap mode is set and called.
@@ -625,10 +1068,22 @@ TEST_F(IoDevTestSuite, NodesLeftRightSwappedCallback) {
memset(&iodev, 0, sizeof(iodev));
memset(&ionode, 0, sizeof(ionode));
ionode.dev = &iodev;
- cras_iodev_list_set_node_left_right_swapped_callbacks(
- node_left_right_swapped_cb);
cras_iodev_list_notify_node_left_right_swapped(&ionode);
- EXPECT_EQ(1, node_left_right_swapped_cb_called);
+ EXPECT_EQ(1, cras_observer_notify_node_left_right_swapped_called);
+}
+
+// Test callback function for volume and gain are set and called.
+TEST_F(IoDevTestSuite, VolumeGainCallback) {
+
+ struct cras_iodev iodev;
+ struct cras_ionode ionode;
+ memset(&iodev, 0, sizeof(iodev));
+ memset(&ionode, 0, sizeof(ionode));
+ ionode.dev = &iodev;
+ cras_iodev_list_notify_node_volume(&ionode);
+ cras_iodev_list_notify_node_capture_gain(&ionode);
+ EXPECT_EQ(1, cras_observer_notify_output_node_volume_called);
+ EXPECT_EQ(1, cras_observer_notify_input_node_gain_called);
}
TEST_F(IoDevTestSuite, IodevListSetNodeAttr) {
@@ -679,7 +1134,6 @@ TEST_F(IoDevTestSuite, AddActiveNode) {
d1_.direction = CRAS_STREAM_OUTPUT;
d2_.direction = CRAS_STREAM_OUTPUT;
d3_.direction = CRAS_STREAM_OUTPUT;
- d3_.is_open = cras_iodev_is_open_stub;
rc = cras_iodev_list_add_output(&d1_);
ASSERT_EQ(0, rc);
rc = cras_iodev_list_add_output(&d2_);
@@ -687,7 +1141,6 @@ TEST_F(IoDevTestSuite, AddActiveNode) {
rc = cras_iodev_list_add_output(&d3_);
ASSERT_EQ(0, rc);
- iodev_is_open = 0;
audio_thread_add_open_dev_called = 0;
cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
cras_make_node_id(d3_.info.idx, 1));
@@ -697,7 +1150,6 @@ TEST_F(IoDevTestSuite, AddActiveNode) {
// If a stream is added, the device should be opened.
stream_add_cb(&rstream);
ASSERT_EQ(audio_thread_add_open_dev_called, 1);
- iodev_is_open = 1;
audio_thread_rm_open_dev_called = 0;
audio_thread_drain_stream_return = 10;
stream_rm_cb(&rstream);
@@ -714,7 +1166,6 @@ TEST_F(IoDevTestSuite, AddActiveNode) {
clock_gettime_retspec.tv_sec += 30;
cras_tm_timer_cb(NULL, NULL);
ASSERT_EQ(1, audio_thread_rm_open_dev_called);
- iodev_is_open = 0;
audio_thread_rm_open_dev_called = 0;
cras_iodev_list_rm_output(&d3_);
@@ -734,11 +1185,9 @@ TEST_F(IoDevTestSuite, DrainTimerCancel) {
cras_iodev_list_init();
d1_.direction = CRAS_STREAM_OUTPUT;
- d1_.is_open = cras_iodev_is_open_stub;
rc = cras_iodev_list_add_output(&d1_);
EXPECT_EQ(0, rc);
- iodev_is_open = 0;
audio_thread_add_open_dev_called = 0;
cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT,
cras_make_node_id(d1_.info.idx, 1));
@@ -748,7 +1197,6 @@ TEST_F(IoDevTestSuite, DrainTimerCancel) {
// If a stream is added, the device should be opened.
stream_add_cb(&rstream);
EXPECT_EQ(1, audio_thread_add_open_dev_called);
- iodev_is_open = 1;
audio_thread_rm_open_dev_called = 0;
audio_thread_drain_stream_return = 0;
@@ -809,7 +1257,6 @@ TEST_F(IoDevTestSuite, AddRemovePinnedStream) {
// Add 2 output devices.
d1_.direction = CRAS_STREAM_OUTPUT;
- d1_.is_open = cras_iodev_is_open_stub;
EXPECT_EQ(0, cras_iodev_list_add_output(&d1_));
d2_.direction = CRAS_STREAM_OUTPUT;
EXPECT_EQ(0, cras_iodev_list_add_output(&d2_));
@@ -824,16 +1271,22 @@ TEST_F(IoDevTestSuite, AddRemovePinnedStream) {
EXPECT_EQ(1, audio_thread_add_stream_called);
EXPECT_EQ(&d1_, audio_thread_add_stream_dev);
EXPECT_EQ(&rstream, audio_thread_add_stream_stream);
- iodev_is_open = 1;
+ EXPECT_EQ(1, update_active_node_called);
+ EXPECT_EQ(&d1_, update_active_node_iodev_val[0]);
- // Enable d2, check pinned stream is not added to d2.
- cras_iodev_list_enable_dev(&d2_);
+ // Select d2, check pinned stream is not added to d2.
+ cras_iodev_list_select_node(CRAS_STREAM_OUTPUT,
+ cras_make_node_id(d2_.info.idx, 0));
EXPECT_EQ(1, audio_thread_add_stream_called);
+ EXPECT_EQ(2, update_active_node_called);
+ EXPECT_EQ(&d2_, update_active_node_iodev_val[1]);
// Remove pinned stream from d1, check d1 is closed after stream removed.
EXPECT_EQ(0, stream_rm_cb(&rstream));
EXPECT_EQ(1, cras_iodev_close_called);
EXPECT_EQ(&d1_, cras_iodev_close_dev);
+ EXPECT_EQ(3, update_active_node_called);
+ EXPECT_EQ(&d1_, update_active_node_iodev_val[2]);
}
} // namespace
@@ -854,95 +1307,11 @@ struct cras_server_state *cras_system_state_update_begin() {
void cras_system_state_update_complete() {
}
-int cras_system_register_volume_changed_cb(cras_alert_cb cb, void *arg) {
- volume_changed_cb = cb;
- volume_changed_arg = arg;
- register_volume_changed_cb_called++;
- return 0;
-}
-
-int cras_system_remove_volume_changed_cb(cras_alert_cb cb, void *arg) {
- remove_volume_changed_cb_called++;
- return 0;
-}
-
-int cras_system_register_mute_changed_cb(cras_alert_cb cb, void *arg) {
- mute_changed_cb = cb;
- mute_changed_arg = arg;
- register_mute_changed_cb_called++;
- return 0;
-}
-
-int cras_system_remove_mute_changed_cb(cras_alert_cb cb, void *arg) {
- remove_mute_changed_cb_called++;
- return 0;
-}
-
-int cras_system_register_suspend_cb(cras_alert_cb cb, void *arg)
-{
- suspend_cb = cb;
- register_suspend_cb_called++;
- return 0;
-}
-
-int cras_system_remove_suspend_cb(cras_alert_cb cb, void *arg)
-{
- remove_suspend_cb_called++;
- return 0;
-}
-
int cras_system_get_suspended()
{
return cras_system_get_suspended_val;
}
-int cras_system_register_capture_gain_changed_cb(cras_alert_cb cb, void *arg) {
- capture_gain_changed_cb = cb;
- capture_gain_changed_arg = arg;
- register_capture_gain_changed_cb_called++;
- return 0;
-}
-
-int cras_system_remove_capture_gain_changed_cb(cras_alert_cb cb, void *arg) {
- remove_capture_gain_changed_cb_called++;
- return 0;
-}
-
-int cras_system_register_capture_mute_changed_cb(cras_alert_cb cb, void *arg) {
- capture_mute_changed_cb = cb;
- capture_mute_changed_arg = arg;
- register_capture_mute_changed_cb_called++;
- return 0;
-}
-
-int cras_system_remove_capture_mute_changed_cb(cras_alert_cb cb, void *arg) {
- remove_capture_mute_changed_cb_called++;
- return 0;
-}
-
-struct cras_alert *cras_alert_create(cras_alert_prepare prepare) {
- cras_alert_create_called++;
- return NULL;
-}
-
-int cras_alert_add_callback(struct cras_alert *alert, cras_alert_cb cb,
- void *arg) {
- return 0;
-}
-
-int cras_alert_rm_callback(struct cras_alert *alert, cras_alert_cb cb,
- void *arg) {
- return 0;
-}
-
-void cras_alert_pending(struct cras_alert *alert) {
- cras_alert_pending_called++;
-}
-
-void cras_alert_destroy(struct cras_alert *alert) {
- cras_alert_destroy_called++;
-}
-
struct audio_thread *audio_thread_create() {
return &thread;
}
@@ -1046,7 +1415,11 @@ int cras_iodev_set_node_attr(struct cras_ionode *ionode,
struct cras_iodev *empty_iodev_create(enum CRAS_STREAM_DIRECTION direction) {
dummy_empty_iodev[direction].direction = direction;
- dummy_empty_iodev[direction].is_open = cras_iodev_is_open_stub;
+ dummy_empty_iodev[direction].update_active_node = dummy_update_active_node;
+ if (dummy_empty_iodev[direction].active_node == NULL) {
+ struct cras_ionode *node = (struct cras_ionode *)calloc(1, sizeof(*node));
+ dummy_empty_iodev[direction].active_node = node;
+ }
return &dummy_empty_iodev[direction];
}
@@ -1070,17 +1443,13 @@ void loopback_iodev_destroy(struct cras_iodev *iodev) {
int cras_iodev_open(struct cras_iodev *iodev, unsigned int cb_level)
{
- enum CRAS_STREAM_DIRECTION dir = iodev->direction;
- if (iodev == &dummy_empty_iodev[dir])
- empty_iodev_is_open[dir] = 1;
- iodev_is_open = 1;
- return 0;
+ if (cras_iodev_open_ret[cras_iodev_open_called] == 0)
+ iodev->state = CRAS_IODEV_STATE_OPEN;
+ return cras_iodev_open_ret[cras_iodev_open_called++];
}
int cras_iodev_close(struct cras_iodev *iodev) {
- enum CRAS_STREAM_DIRECTION dir = iodev->direction;
- if (iodev == &dummy_empty_iodev[dir])
- empty_iodev_is_open[dir] = 0;
+ iodev->state = CRAS_IODEV_STATE_CLOSE;
cras_iodev_close_called++;
cras_iodev_close_dev = iodev;
return 0;
@@ -1091,6 +1460,22 @@ int cras_iodev_set_format(struct cras_iodev *iodev,
return 0;
}
+int cras_iodev_set_mute(struct cras_iodev* iodev) {
+ set_mute_called++;
+ set_mute_dev_vector.push_back(iodev);
+ return 0;
+}
+
+int cras_iodev_is_zero_volume(const struct cras_iodev *iodev)
+{
+ return cras_iodev_is_zero_volume_ret;
+}
+
+enum CRAS_IODEV_STATE cras_iodev_state(const struct cras_iodev *iodev)
+{
+ return cras_iodev_state_ret[iodev];
+}
+
struct stream_list *stream_list_create(stream_callback *add_cb,
stream_callback *rm_cb,
stream_create_func *create_cb,
@@ -1126,10 +1511,83 @@ struct cras_timer *cras_tm_create_timer(
void (*cb)(struct cras_timer *t, void *data),
void *cb_data) {
cras_tm_timer_cb = cb;
+ cras_tm_timer_cb_data = cb_data;
+ cras_tm_create_timer_called++;
return reinterpret_cast<struct cras_timer *>(0x404);
}
void cras_tm_cancel_timer(struct cras_tm *tm, struct cras_timer *t) {
+ cras_tm_cancel_timer_called++;
+}
+
+void cras_fmt_conv_destroy(struct cras_fmt_conv *conv)
+{
+}
+
+struct cras_fmt_conv *cras_channel_remix_conv_create(
+ unsigned int num_channels, const float *coefficient)
+{
+ return NULL;
+}
+
+void cras_channel_remix_convert(struct cras_fmt_conv *conv,
+ uint8_t *in_buf, size_t frames)
+{
+}
+
+struct cras_observer_client *cras_observer_add(
+ const struct cras_observer_ops *ops,
+ void *context)
+{
+ observer_ops = (struct cras_observer_ops *)calloc(1, sizeof(*ops));
+ memcpy(observer_ops, ops, sizeof(*ops));
+ cras_observer_add_called++;
+ return reinterpret_cast<struct cras_observer_client *>(0x55);
+}
+
+void cras_observer_remove(struct cras_observer_client *client)
+{
+ if (observer_ops)
+ free(observer_ops);
+ cras_observer_remove_called++;
+}
+
+void cras_observer_notify_nodes(void) {
+ cras_observer_notify_nodes_called++;
+}
+
+void cras_observer_notify_active_node(enum CRAS_STREAM_DIRECTION direction,
+ cras_node_id_t node_id)
+{
+ cras_observer_notify_active_node_called++;
+}
+
+void cras_observer_notify_output_node_volume(cras_node_id_t node_id,
+ int32_t volume)
+{
+ cras_observer_notify_output_node_volume_called++;
+}
+
+void cras_observer_notify_node_left_right_swapped(cras_node_id_t node_id,
+ int swapped)
+{
+ cras_observer_notify_node_left_right_swapped_called++;
+}
+
+void cras_observer_notify_input_node_gain(cras_node_id_t node_id,
+ int32_t gain)
+{
+ cras_observer_notify_input_node_gain_called++;
+}
+
+int audio_thread_dev_start_ramp(struct audio_thread *thread,
+ struct cras_iodev *dev,
+ enum CRAS_IODEV_RAMP_REQUEST request)
+{
+ audio_thread_dev_start_ramp_called++;
+ audio_thread_dev_start_ramp_dev = dev;
+ audio_thread_dev_start_ramp_req = request;
+ return 0;
}
// From librt.
diff --git a/cras/src/tests/iodev_unittest.cc b/cras/src/tests/iodev_unittest.cc
index 8f8e0ad6..29e6e22e 100644
--- a/cras/src/tests/iodev_unittest.cc
+++ b/cras/src/tests/iodev_unittest.cc
@@ -7,14 +7,23 @@
extern "C" {
#include "cras_iodev.h"
+#include "cras_ramp.h"
#include "cras_rstream.h"
#include "dev_stream.h"
#include "utlist.h"
+#include "cras_audio_area.h"
+#include "audio_thread_log.h"
// Mock software volume scalers.
float softvol_scalers[101];
}
+#define BUFFER_SIZE 8192
+
+static const float RAMP_UNMUTE_DURATION_SECS = 0.5;
+static const float RAMP_NEW_STREAM_DURATION_SECS = 0.01;
+static const float RAMP_MUTE_DURATION_SECS = 0.1;
+
static int cras_iodev_list_disable_dev_called;
static int select_node_called;
static enum CRAS_STREAM_DIRECTION select_node_direction;
@@ -53,13 +62,47 @@ static unsigned int rate_estimator_add_frames_called;
static int cras_system_get_mute_return;
static snd_pcm_format_t cras_scale_buffer_fmt;
static float cras_scale_buffer_scaler;
+static int cras_scale_buffer_called;
static unsigned int pre_dsp_hook_called;
static const uint8_t *pre_dsp_hook_frames;
+
static void *pre_dsp_hook_cb_data;
static unsigned int post_dsp_hook_called;
static const uint8_t *post_dsp_hook_frames;
static void *post_dsp_hook_cb_data;
static int iodev_buffer_size;
+static long cras_system_get_capture_gain_ret_value;
+static uint8_t audio_buffer[BUFFER_SIZE];
+static struct cras_audio_area *audio_area;
+static unsigned int put_buffer_nframes;
+static int output_should_wake_ret;
+static int no_stream_called;
+static int no_stream_enable;
+// This will be used extensively in cras_iodev.
+struct audio_thread_event_log *atlog;
+static unsigned int simple_no_stream_called;
+static int simple_no_stream_enable;
+static int dev_stream_playback_frames_ret;
+static int get_num_underruns_ret;
+static int device_monitor_reset_device_called;
+static int output_underrun_called;
+static int set_mute_called;
+static int cras_ramp_start_is_up;
+static int cras_ramp_start_duration_frames;
+static int cras_ramp_start_is_called;
+static int cras_ramp_reset_is_called;
+static struct cras_ramp_action cras_ramp_get_current_action_ret;
+static int cras_ramp_update_ramped_frames_num_frames;
+static cras_ramp_cb cras_ramp_start_cb;
+static void* cras_ramp_start_cb_data;
+static int cras_device_monitor_set_device_mute_state_called;
+static struct cras_iodev* cras_device_monitor_set_device_mute_state_dev;
+static snd_pcm_format_t cras_scale_buffer_increment_fmt;
+static uint8_t *cras_scale_buffer_increment_buff;
+static unsigned int cras_scale_buffer_increment_frame;
+static float cras_scale_buffer_increment_scaler;
+static float cras_scale_buffer_increment_increment;
+static int cras_scale_buffer_increment_channel;
// Iodev callback
int update_channel_layout(struct cras_iodev *iodev) {
@@ -108,12 +151,49 @@ void ResetStubData() {
rate_estimator_add_frames_num_frames = 0;
rate_estimator_add_frames_called = 0;
cras_system_get_mute_return = 0;
+ cras_system_get_volume_return = 100;
cras_mix_mute_count = 0;
pre_dsp_hook_called = 0;
pre_dsp_hook_frames = NULL;
post_dsp_hook_called = 0;
- post_dsp_hook_frames = NULL;
- iodev_buffer_size = 0;
+ post_dsp_hook_frames = NULL; iodev_buffer_size = 0;
+ cras_system_get_capture_gain_ret_value = 0;
+ // Assume there is some data in audio buffer.
+ memset(audio_buffer, 0xff, sizeof(audio_buffer));
+ if (audio_area) {
+ free(audio_area);
+ audio_area = NULL;
+ }
+ put_buffer_nframes = 0;
+ output_should_wake_ret= 0;
+ no_stream_called = 0;
+ no_stream_enable = 0;
+ simple_no_stream_called = 0;
+ simple_no_stream_enable = 0;
+ dev_stream_playback_frames_ret = 0;
+ if (!atlog)
+ atlog = audio_thread_event_log_init();
+ get_num_underruns_ret = 0;
+ device_monitor_reset_device_called = 0;
+ output_underrun_called = 0;
+ set_mute_called = 0;
+ cras_ramp_start_is_up = 0;
+ cras_ramp_start_duration_frames = 0;
+ cras_ramp_start_cb = NULL;
+ cras_ramp_start_cb_data = NULL;
+ cras_ramp_start_is_called = 0;
+ cras_ramp_reset_is_called = 0;
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_NONE;
+ cras_ramp_update_ramped_frames_num_frames = 0;
+ cras_device_monitor_set_device_mute_state_called = 0;
+ cras_device_monitor_set_device_mute_state_dev = NULL;
+ cras_scale_buffer_called = 0;
+ cras_scale_buffer_increment_fmt = SND_PCM_FORMAT_UNKNOWN;
+ cras_scale_buffer_increment_buff = NULL;
+ cras_scale_buffer_increment_frame = 0;
+ cras_scale_buffer_increment_scaler = 0;
+ cras_scale_buffer_increment_increment = 0;
+ cras_scale_buffer_increment_channel = 0;
}
namespace {
@@ -434,14 +514,47 @@ TEST_F(IoDevSetFormatTestSuite, UpdateChannelLayoutFail6ch) {
// Put buffer tests
-static unsigned int put_buffer_nframes;
+static int get_buffer(cras_iodev* iodev, struct cras_audio_area** area,
+ unsigned int* num) {
+ size_t sz = sizeof(*audio_area) + sizeof(struct cras_channel_area) * 2;
+
+ audio_area = (cras_audio_area*)calloc(1, sz);
+ audio_area->frames = *num;
+ audio_area->num_channels = 2;
+ audio_area->channels[0].buf = audio_buffer;
+ channel_area_set_channel(&audio_area->channels[0], CRAS_CH_FL);
+ audio_area->channels[0].step_bytes = 4;
+ audio_area->channels[1].buf = audio_buffer + 2;
+ channel_area_set_channel(&audio_area->channels[1], CRAS_CH_FR);
+ audio_area->channels[1].step_bytes = 4;
+
+ *area = audio_area;
+ return 0;
+}
static int put_buffer(struct cras_iodev *iodev, unsigned int nframes)
{
put_buffer_nframes = nframes;
+ if (audio_area) {
+ free(audio_area);
+ audio_area = NULL;
+ }
return 0;
}
+static int no_stream(struct cras_iodev *odev, int enable)
+{
+ no_stream_called++;
+ no_stream_enable = enable;
+ // Use default no stream playback to test default behavior.
+ return cras_iodev_default_no_stream_playback(odev, enable);
+}
+
+static int output_should_wake(const struct cras_iodev *odev)
+{
+ return output_should_wake_ret;
+}
+
static int pre_dsp_hook(const uint8_t *frames, unsigned int nframes,
const struct cras_audio_format *fmt, void *cb_data)
{
@@ -483,14 +596,173 @@ TEST(IoDevPutOutputBuffer, SystemMuted) {
EXPECT_EQ(20, rate_estimator_add_frames_num_frames);
}
+TEST(IoDevPutOutputBuffer, MuteForVolume) {
+ struct cras_iodev iodev;
+ struct cras_ionode ionode;
+
+ ResetStubData();
+ memset(&iodev, 0, sizeof(iodev));
+ memset(&ionode, 0, sizeof(ionode));
+
+ iodev.nodes = &ionode;
+ iodev.active_node = &ionode;
+ iodev.active_node->dev = &iodev;
+
+ // Case: System volume 100; Node volume 0. => Mute
+ cras_system_get_volume_return = 100;
+ iodev.active_node->volume = 0;
+ EXPECT_EQ(1, cras_iodev_is_zero_volume(&iodev));
+
+ // Case: System volume 100; Node volume 50. => Not mute
+ cras_system_get_volume_return = 100;
+ iodev.active_node->volume = 50;
+ EXPECT_EQ(0, cras_iodev_is_zero_volume(&iodev));
+
+ // Case: System volume 0; Node volume 50. => Mute
+ cras_system_get_volume_return = 0;
+ iodev.active_node->volume = 50;
+ EXPECT_EQ(1, cras_iodev_is_zero_volume(&iodev));
+
+ // Case: System volume 50; Node volume 50. => Mute
+ cras_system_get_volume_return = 50;
+ iodev.active_node->volume = 50;
+ EXPECT_EQ(1, cras_iodev_is_zero_volume(&iodev));
+}
+
+TEST(IoDevPutOutputBuffer, NodeVolumeZeroShouldMute) {
+ struct cras_audio_format fmt;
+ struct cras_iodev iodev;
+ struct cras_ionode ionode;
+ uint8_t *frames = reinterpret_cast<uint8_t*>(0x44);
+ int rc;
+
+ ResetStubData();
+ memset(&iodev, 0, sizeof(iodev));
+ memset(&ionode, 0, sizeof(ionode));
+
+ iodev.nodes = &ionode;
+ iodev.active_node = &ionode;
+ iodev.active_node->dev = &iodev;
+ iodev.active_node->volume = 0;
+
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+ iodev.format = &fmt;
+ iodev.put_buffer = put_buffer;
+
+ rc = cras_iodev_put_output_buffer(&iodev, frames, 20);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(20, cras_mix_mute_count);
+ EXPECT_EQ(20, put_buffer_nframes);
+ EXPECT_EQ(20, rate_estimator_add_frames_num_frames);
+}
+
+TEST(IoDevPutOutputBuffer, SystemMutedWithRamp) {
+ struct cras_audio_format fmt;
+ struct cras_iodev iodev;
+ uint8_t *frames = reinterpret_cast<uint8_t*>(0x44);
+ int rc;
+
+ ResetStubData();
+ memset(&iodev, 0, sizeof(iodev));
+ cras_system_get_mute_return = 1;
+
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+ iodev.format = &fmt;
+ iodev.put_buffer = put_buffer;
+
+ // Assume device has ramp member.
+ iodev.ramp = reinterpret_cast<struct cras_ramp*>(0x1);
+
+ // Assume ramping is done.
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_NONE;
+
+ rc = cras_iodev_put_output_buffer(&iodev, frames, 20);
+ // Output should be muted.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(20, cras_mix_mute_count);
+ EXPECT_EQ(20, put_buffer_nframes);
+ EXPECT_EQ(20, rate_estimator_add_frames_num_frames);
+
+ // Test for the case where ramping is not done yet.
+ ResetStubData();
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_PARTIAL;
+ rc = cras_iodev_put_output_buffer(&iodev, frames, 20);
+
+ // Output should not be muted.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_mix_mute_count);
+ // Ramped frames should be increased by 20.
+ EXPECT_EQ(20, cras_ramp_update_ramped_frames_num_frames);
+ EXPECT_EQ(20, put_buffer_nframes);
+ EXPECT_EQ(20, rate_estimator_add_frames_num_frames);
+}
+
+TEST(IoDevPutOutputBuffer, NodeVolumeZeroShouldMuteWithRamp) {
+ struct cras_audio_format fmt;
+ struct cras_iodev iodev;
+ struct cras_ionode ionode;
+ uint8_t *frames = reinterpret_cast<uint8_t*>(0x44);
+ int rc;
+
+ ResetStubData();
+ memset(&iodev, 0, sizeof(iodev));
+ memset(&ionode, 0, sizeof(ionode));
+
+ iodev.nodes = &ionode;
+ iodev.active_node = &ionode;
+ iodev.active_node->dev = &iodev;
+ iodev.active_node->volume = 0;
+
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+ iodev.format = &fmt;
+ iodev.put_buffer = put_buffer;
+
+ // Assume device has ramp member.
+ iodev.ramp = reinterpret_cast<struct cras_ramp*>(0x1);
+
+ // Assume ramping is done.
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_NONE;
+
+ rc = cras_iodev_put_output_buffer(&iodev, frames, 20);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(20, cras_mix_mute_count);
+ EXPECT_EQ(20, put_buffer_nframes);
+ EXPECT_EQ(20, rate_estimator_add_frames_num_frames);
+
+ // Test for the case where ramping is not done yet.
+ ResetStubData();
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_PARTIAL;
+ rc = cras_iodev_put_output_buffer(&iodev, frames, 20);
+
+ // Output should not be muted.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_mix_mute_count);
+ // Ramped frames should be increased by 20.
+ EXPECT_EQ(20, cras_ramp_update_ramped_frames_num_frames);
+ EXPECT_EQ(20, put_buffer_nframes);
+ EXPECT_EQ(20, rate_estimator_add_frames_num_frames);
+}
TEST(IoDevPutOutputBuffer, NoDSP) {
struct cras_audio_format fmt;
struct cras_iodev iodev;
+ struct cras_ionode ionode;
uint8_t *frames = reinterpret_cast<uint8_t*>(0x44);
int rc;
ResetStubData();
memset(&iodev, 0, sizeof(iodev));
+ memset(&ionode, 0, sizeof(ionode));
+
+ iodev.nodes = &ionode;
+ iodev.active_node = &ionode;
+ iodev.active_node->dev = &iodev;
+ iodev.active_node->volume = 100;
fmt.format = SND_PCM_FORMAT_S16_LE;
fmt.frame_rate = 48000;
@@ -566,6 +838,132 @@ TEST(IoDevPutOutputBuffer, SoftVol) {
EXPECT_EQ(SND_PCM_FORMAT_S16_LE, cras_scale_buffer_fmt);
}
+TEST(IoDevPutOutputBuffer, SoftVolWithRamp) {
+ struct cras_audio_format fmt;
+ struct cras_iodev iodev;
+ uint8_t *frames = reinterpret_cast<uint8_t*>(0x44);
+ int rc;
+ int n_frames = 53;
+ float ramp_scaler = 0.2;
+ float increment = 0.001;
+ int volume = 13;
+ float volume_scaler = 0.435;
+
+ ResetStubData();
+ memset(&iodev, 0, sizeof(iodev));
+ iodev.software_volume_needed = 1;
+
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+ iodev.format = &fmt;
+ iodev.put_buffer = put_buffer;
+ // Assume device has ramp member.
+ iodev.ramp = reinterpret_cast<struct cras_ramp*>(0x1);
+
+ // Assume ramping is done.
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_NONE;
+
+ cras_system_get_volume_return = volume;
+ softvol_scalers[volume] = volume_scaler;
+
+ rc = cras_iodev_put_output_buffer(&iodev, frames, n_frames);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_mix_mute_count);
+ EXPECT_EQ(n_frames, put_buffer_nframes);
+ EXPECT_EQ(n_frames, rate_estimator_add_frames_num_frames);
+ EXPECT_EQ(softvol_scalers[volume], cras_scale_buffer_scaler);
+ EXPECT_EQ(SND_PCM_FORMAT_S16_LE, cras_scale_buffer_fmt);
+
+ ResetStubData();
+ // Assume ramping is not done.
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_PARTIAL;
+ cras_ramp_get_current_action_ret.scaler = ramp_scaler;
+ cras_ramp_get_current_action_ret.increment = increment;
+
+ cras_system_get_volume_return = volume;
+ softvol_scalers[volume] = volume_scaler;
+
+ rc = cras_iodev_put_output_buffer(&iodev, frames, n_frames);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_mix_mute_count);
+ // cras_scale_buffer is not called.
+ EXPECT_EQ(0, cras_scale_buffer_called);
+
+ // Verify the arguments passed to cras_scale_buffer_increment.
+ EXPECT_EQ(fmt.format, cras_scale_buffer_increment_fmt);
+ EXPECT_EQ(frames, cras_scale_buffer_increment_buff);
+ EXPECT_EQ(n_frames, cras_scale_buffer_increment_frame);
+ // Initial scaler will be product of software volume scaler and
+ // ramp scaler.
+ EXPECT_FLOAT_EQ(softvol_scalers[volume] * ramp_scaler,
+ cras_scale_buffer_increment_scaler);
+ // Increment scaler will be product of software volume scaler and
+ // ramp increment.
+ EXPECT_FLOAT_EQ(softvol_scalers[volume] * increment,
+ cras_scale_buffer_increment_increment);
+ EXPECT_EQ(fmt.num_channels, cras_scale_buffer_increment_channel);
+
+ EXPECT_EQ(n_frames, put_buffer_nframes);
+ EXPECT_EQ(n_frames, rate_estimator_add_frames_num_frames);
+}
+
+TEST(IoDevPutOutputBuffer, NoSoftVolWithRamp) {
+ struct cras_audio_format fmt;
+ struct cras_iodev iodev;
+ uint8_t *frames = reinterpret_cast<uint8_t*>(0x44);
+ int rc;
+ int n_frames = 53;
+ float ramp_scaler = 0.2;
+ float increment = 0.001;
+
+ ResetStubData();
+ memset(&iodev, 0, sizeof(iodev));
+ iodev.software_volume_needed = 0;
+
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+ iodev.format = &fmt;
+ iodev.put_buffer = put_buffer;
+ // Assume device has ramp member.
+ iodev.ramp = reinterpret_cast<struct cras_ramp*>(0x1);
+
+ // Assume ramping is done.
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_NONE;
+
+ rc = cras_iodev_put_output_buffer(&iodev, frames, n_frames);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_mix_mute_count);
+ // cras_scale_buffer is not called.
+ EXPECT_EQ(0, cras_scale_buffer_called);
+ EXPECT_EQ(n_frames, put_buffer_nframes);
+ EXPECT_EQ(n_frames, rate_estimator_add_frames_num_frames);
+
+ ResetStubData();
+ // Assume ramping is not done.
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_PARTIAL;
+ cras_ramp_get_current_action_ret.scaler = ramp_scaler;
+ cras_ramp_get_current_action_ret.increment = increment;
+
+ rc = cras_iodev_put_output_buffer(&iodev, frames, n_frames);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_mix_mute_count);
+ // cras_scale_buffer is not called.
+ EXPECT_EQ(0, cras_scale_buffer_called);
+
+ // Verify the arguments passed to cras_scale_buffer_increment.
+ EXPECT_EQ(fmt.format, cras_scale_buffer_increment_fmt);
+ EXPECT_EQ(frames, cras_scale_buffer_increment_buff);
+ EXPECT_EQ(n_frames, cras_scale_buffer_increment_frame);
+ EXPECT_FLOAT_EQ(ramp_scaler, cras_scale_buffer_increment_scaler);
+ EXPECT_FLOAT_EQ(increment, cras_scale_buffer_increment_increment);
+ EXPECT_EQ(fmt.num_channels, cras_scale_buffer_increment_channel);
+
+ EXPECT_EQ(n_frames, put_buffer_nframes);
+ EXPECT_EQ(n_frames, rate_estimator_add_frames_num_frames);
+}
+
TEST(IoDevPutOutputBuffer, Scale32Bit) {
struct cras_audio_format fmt;
struct cras_iodev iodev;
@@ -597,13 +995,16 @@ TEST(IoDevPutOutputBuffer, Scale32Bit) {
static unsigned fr_queued = 0;
-static int frames_queued(const struct cras_iodev *iodev)
+static int frames_queued(const struct cras_iodev *iodev,
+ struct timespec *tstamp)
{
+ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
return fr_queued;
}
TEST(IoDevQueuedBuffer, ZeroMinBufferLevel) {
struct cras_iodev iodev;
+ struct timespec tstamp;
int rc;
ResetStubData();
@@ -614,7 +1015,7 @@ TEST(IoDevQueuedBuffer, ZeroMinBufferLevel) {
iodev.buffer_size = 200;
fr_queued = 50;
- rc = cras_iodev_frames_queued(&iodev);
+ rc = cras_iodev_frames_queued(&iodev, &tstamp);
EXPECT_EQ(50, rc);
rc = cras_iodev_buffer_avail(&iodev, rc);
EXPECT_EQ(150, rc);
@@ -622,6 +1023,7 @@ TEST(IoDevQueuedBuffer, ZeroMinBufferLevel) {
TEST(IoDevQueuedBuffer, NonZeroMinBufferLevel) {
struct cras_iodev iodev;
+ struct timespec hw_tstamp;
int rc;
ResetStubData();
@@ -632,21 +1034,22 @@ TEST(IoDevQueuedBuffer, NonZeroMinBufferLevel) {
iodev.buffer_size = 200;
fr_queued = 180;
- rc = cras_iodev_frames_queued(&iodev);
+ rc = cras_iodev_frames_queued(&iodev, &hw_tstamp);
EXPECT_EQ(80, rc);
rc = cras_iodev_buffer_avail(&iodev, rc);
EXPECT_EQ(20, rc);
/* When fr_queued < min_buffer_level*/
fr_queued = 80;
- rc = cras_iodev_frames_queued(&iodev);
+ rc = cras_iodev_frames_queued(&iodev, &hw_tstamp);
EXPECT_EQ(0, rc);
rc = cras_iodev_buffer_avail(&iodev, rc);
EXPECT_EQ(100, rc);
}
static void update_active_node(struct cras_iodev *iodev,
- unsigned node_idx)
+ unsigned node_idx,
+ unsigned dev_enabled)
{
}
@@ -658,20 +1061,36 @@ static void dev_set_capture_gain(struct cras_iodev *iodev)
{
}
+static void dev_set_mute(struct cras_iodev *iodev)
+{
+ set_mute_called++;
+}
+
TEST(IoNodePlug, PlugUnplugNode) {
struct cras_iodev iodev;
- struct cras_ionode ionode;
+ struct cras_ionode ionode, ionode2;
memset(&iodev, 0, sizeof(iodev));
memset(&ionode, 0, sizeof(ionode));
- ionode.dev = &iodev;
+ memset(&ionode2, 0, sizeof(ionode2));
iodev.direction = CRAS_STREAM_INPUT;
iodev.update_active_node = update_active_node;
+ ionode.dev = &iodev;
+ cras_iodev_add_node(&iodev, &ionode);
+ ionode2.dev = &iodev;
+ cras_iodev_add_node(&iodev, &ionode2);
+ cras_iodev_set_active_node(&iodev, &ionode);
ResetStubData();
cras_iodev_set_node_attr(&ionode, IONODE_ATTR_PLUGGED, 1);
EXPECT_EQ(0, cras_iodev_list_disable_dev_called);
cras_iodev_set_node_attr(&ionode, IONODE_ATTR_PLUGGED, 0);
EXPECT_EQ(1, cras_iodev_list_disable_dev_called);
+
+ /* Unplug non-active node shouldn't disable iodev. */
+ cras_iodev_set_node_attr(&ionode2, IONODE_ATTR_PLUGGED, 1);
+ EXPECT_EQ(1, cras_iodev_list_disable_dev_called);
+ cras_iodev_set_node_attr(&ionode2, IONODE_ATTR_PLUGGED, 0);
+ EXPECT_EQ(1, cras_iodev_list_disable_dev_called);
}
TEST(IoDev, AddRemoveNode) {
@@ -738,6 +1157,24 @@ TEST(IoDev, SetNodeSwapLeftRight) {
EXPECT_EQ(2, notify_node_left_right_swapped_called);
}
+TEST(IoDev, SetMute) {
+ struct cras_iodev iodev;
+ int rc;
+
+ memset(&iodev, 0, sizeof(iodev));
+ iodev.set_mute = dev_set_mute;
+ iodev.state = CRAS_IODEV_STATE_CLOSE;
+
+ ResetStubData();
+ rc = cras_iodev_set_mute(&iodev);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, set_mute_called);
+
+ iodev.state = CRAS_IODEV_STATE_OPEN;
+ rc = cras_iodev_set_mute(&iodev);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, set_mute_called);
+}
// Test software volume changes for default output.
TEST(IoDev, SoftwareVolume) {
@@ -770,11 +1207,165 @@ TEST(IoDev, SoftwareVolume) {
EXPECT_FLOAT_EQ(0.3, cras_iodev_get_software_volume_scaler(&iodev));
}
+// Test software gain scaler.
+TEST(IoDev, SoftwareGain) {
+ struct cras_iodev iodev;
+ struct cras_ionode ionode;
+
+ memset(&iodev, 0, sizeof(iodev));
+ memset(&ionode, 0, sizeof(ionode));
+ ResetStubData();
+
+ iodev.nodes = &ionode;
+ iodev.active_node = &ionode;
+ iodev.active_node->dev = &iodev;
+
+ ionode.capture_gain= 400;
+ ionode.software_volume_needed = 1;
+ ionode.max_software_gain = 3000;
+
+ // Check that system volume changes software volume if needed.
+ cras_system_get_capture_gain_ret_value = 2000;
+ // system_gain + node_gain = 2000 + 400 = 2400
+ // 2400 dBm is 15.848931
+ EXPECT_FLOAT_EQ(15.848931, cras_iodev_get_software_gain_scaler(&iodev));
+ EXPECT_FLOAT_EQ(3000, cras_iodev_maximum_software_gain(&iodev));
+
+ // Software gain scaler should be 1.0 if software gain is not needed.
+ ionode.software_volume_needed = 0;
+ EXPECT_FLOAT_EQ(1.0, cras_iodev_get_software_gain_scaler(&iodev));
+ EXPECT_FLOAT_EQ(0, cras_iodev_maximum_software_gain(&iodev));
+}
+
+// This get_buffer implementation set returned frames larger than requested
+// frames.
+static int bad_get_buffer(struct cras_iodev *iodev,
+ struct cras_audio_area **area,
+ unsigned *frames)
+{
+ *frames = *frames + 1;
+ return 0;
+}
+
+// Check that if get_buffer implementation returns invalid frames,
+// cras_iodev_get_output_buffer and cras_iodev_get_input_buffer can return
+// error.
+TEST(IoDev, GetBufferInvalidFrames) {
+ struct cras_iodev iodev;
+ struct cras_audio_area **area = NULL;
+ unsigned int frames = 512;
+ struct cras_audio_format fmt;
+
+ // Format is used in cras_iodev_get_input_buffer;
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+
+ memset(&iodev, 0, sizeof(iodev));
+
+ ResetStubData();
+
+ iodev.format = &fmt;
+ iodev.get_buffer = bad_get_buffer;
+
+ EXPECT_EQ(-EINVAL, cras_iodev_get_output_buffer(&iodev, area, &frames));
+ EXPECT_EQ(-EINVAL, cras_iodev_get_input_buffer(&iodev, area, &frames));
+}
+
static int open_dev(struct cras_iodev *iodev) {
iodev->buffer_size = iodev_buffer_size;
return 0;
}
+TEST(IoDev, OpenOutputDeviceNoStart) {
+ struct cras_iodev iodev;
+
+ memset(&iodev, 0, sizeof(iodev));
+ iodev.open_dev = open_dev;
+ iodev.direction = CRAS_STREAM_OUTPUT;
+ ResetStubData();
+
+ iodev.state = CRAS_IODEV_STATE_CLOSE;
+
+ iodev_buffer_size = 1024;
+ cras_iodev_open(&iodev, 240);
+ EXPECT_EQ(0, iodev.max_cb_level);
+ EXPECT_EQ(240, iodev.min_cb_level);
+
+ // Test that state is no stream run when there is no start ops.
+ EXPECT_EQ(CRAS_IODEV_STATE_NO_STREAM_RUN, iodev.state);
+}
+
+int fake_start(const struct cras_iodev *iodev) {
+ return 0;
+}
+
+TEST(IoDev, OpenOutputDeviceWithStart) {
+ struct cras_iodev iodev;
+
+ memset(&iodev, 0, sizeof(iodev));
+ iodev.open_dev = open_dev;
+ iodev.direction = CRAS_STREAM_OUTPUT;
+ ResetStubData();
+
+ iodev.state = CRAS_IODEV_STATE_CLOSE;
+ iodev.start = fake_start;
+
+ iodev_buffer_size = 1024;
+ cras_iodev_open(&iodev, 240);
+ EXPECT_EQ(0, iodev.max_cb_level);
+ EXPECT_EQ(240, iodev.min_cb_level);
+
+ // Test that state is no stream run when there is start ops.
+ EXPECT_EQ(CRAS_IODEV_STATE_OPEN, iodev.state);
+}
+
+TEST(IoDev, OpenInputDeviceNoStart) {
+ struct cras_iodev iodev;
+
+ memset(&iodev, 0, sizeof(iodev));
+ iodev.open_dev = open_dev;
+ iodev.direction = CRAS_STREAM_INPUT;
+ ResetStubData();
+
+ iodev.state = CRAS_IODEV_STATE_CLOSE;
+
+ iodev_buffer_size = 1024;
+ cras_iodev_open(&iodev, 240);
+ EXPECT_EQ(0, iodev.max_cb_level);
+ EXPECT_EQ(240, iodev.min_cb_level);
+
+ // Test that state is normal run when there is start ops.
+ EXPECT_EQ(CRAS_IODEV_STATE_NORMAL_RUN, iodev.state);
+}
+
+TEST(IoDev, OpenInputDeviceWithStart) {
+ struct cras_iodev iodev;
+
+ memset(&iodev, 0, sizeof(iodev));
+ iodev.open_dev = open_dev;
+ iodev.direction = CRAS_STREAM_INPUT;
+ ResetStubData();
+
+ iodev.state = CRAS_IODEV_STATE_CLOSE;
+ iodev.start = fake_start;
+
+ iodev_buffer_size = 1024;
+ cras_iodev_open(&iodev, 240);
+ EXPECT_EQ(0, iodev.max_cb_level);
+ EXPECT_EQ(240, iodev.min_cb_level);
+
+ // Test that state is normal run even if there is start ops.
+ EXPECT_EQ(CRAS_IODEV_STATE_NORMAL_RUN, iodev.state);
+}
+
+static int simple_no_stream(struct cras_iodev *dev, int enable)
+{
+ simple_no_stream_enable = enable;
+ simple_no_stream_called++;
+ return 0;
+}
+
TEST(IoDev, AddRmStream) {
struct cras_iodev iodev;
struct cras_rstream rstream1, rstream2;
@@ -782,6 +1373,8 @@ TEST(IoDev, AddRmStream) {
memset(&iodev, 0, sizeof(iodev));
iodev.open_dev = open_dev;
+ iodev.no_stream = simple_no_stream;
+ iodev.state = CRAS_IODEV_STATE_NORMAL_RUN;
rstream1.cb_threshold = 800;
stream1.stream = &rstream1;
rstream2.cb_threshold = 400;
@@ -805,6 +1398,7 @@ TEST(IoDev, AddRmStream) {
cras_iodev_rm_stream(&iodev, &rstream1);
EXPECT_EQ(400, iodev.max_cb_level);
EXPECT_EQ(400, iodev.min_cb_level);
+ EXPECT_EQ(0, simple_no_stream_called);
/* When all streams are removed, keep the last min_cb_level for draining. */
cras_iodev_rm_stream(&iodev, &rstream2);
@@ -812,6 +1406,557 @@ TEST(IoDev, AddRmStream) {
EXPECT_EQ(400, iodev.min_cb_level);
}
+TEST(IoDev, FillZeros) {
+ struct cras_iodev iodev;
+ struct cras_audio_format fmt;
+ unsigned int frames = 50;
+ int16_t *zeros;
+ int rc;
+
+ ResetStubData();
+
+ memset(&iodev, 0, sizeof(iodev));
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+ iodev.ext_format = &fmt;
+ iodev.get_buffer = get_buffer;
+ iodev.put_buffer = put_buffer;
+
+ iodev.direction = CRAS_STREAM_INPUT;
+ rc = cras_iodev_fill_odev_zeros(&iodev, frames);
+ EXPECT_EQ(-EINVAL, rc);
+
+ iodev.direction = CRAS_STREAM_OUTPUT;
+ rc = cras_iodev_fill_odev_zeros(&iodev, frames);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(frames, put_buffer_nframes);
+ zeros = (int16_t *)calloc(frames * 2, sizeof(*zeros));
+ rc = memcmp(audio_buffer, zeros, frames * 2 * 2);
+ free(zeros);
+ EXPECT_EQ(0, rc);
+}
+
+TEST(IoDev, DefaultNoStreamPlaybackRunning) {
+ struct cras_iodev iodev;
+ struct cras_audio_format fmt;
+ unsigned int hw_level = 50;
+ unsigned int min_cb_level = 240;
+ unsigned int zeros_to_fill;
+ int16_t *zeros;
+ int rc;
+
+ memset(&iodev, 0, sizeof(iodev));
+
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+ iodev.ext_format = &fmt;
+ iodev.min_cb_level = min_cb_level;
+ iodev.get_buffer = get_buffer;
+ iodev.put_buffer = put_buffer;
+ iodev.frames_queued = frames_queued;
+ iodev.min_buffer_level = 0;
+ iodev.direction = CRAS_STREAM_OUTPUT;
+ iodev.buffer_size = BUFFER_SIZE;
+ iodev.no_stream = no_stream;
+
+ ResetStubData();
+
+ // Device is running. hw_level is less than target.
+ // Need to fill to callback level * 2;
+ iodev.state = CRAS_IODEV_STATE_NO_STREAM_RUN;
+ fr_queued = hw_level;
+ zeros_to_fill = min_cb_level * 2 - hw_level;
+
+ rc = cras_iodev_default_no_stream_playback(&iodev, 1);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(CRAS_IODEV_STATE_NO_STREAM_RUN, iodev.state);
+ EXPECT_EQ(zeros_to_fill, put_buffer_nframes);
+ zeros = (int16_t *)calloc(zeros_to_fill * 2, sizeof(*zeros));
+ EXPECT_EQ(0, memcmp(audio_buffer, zeros, zeros_to_fill * 2 * 2));
+ free(zeros);
+
+ ResetStubData();
+
+ // Device is running. hw_level is not less than target.
+ // No need to fill zeros.
+ iodev.state = CRAS_IODEV_STATE_NO_STREAM_RUN;
+ hw_level = min_cb_level * 2;
+ fr_queued = hw_level;
+ zeros_to_fill = 0;
+
+ rc = cras_iodev_default_no_stream_playback(&iodev, 1);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(CRAS_IODEV_STATE_NO_STREAM_RUN, iodev.state);
+ EXPECT_EQ(zeros_to_fill, put_buffer_nframes);
+}
+
+TEST(IoDev, PrepareOutputBeforeWriteSamples) {
+ struct cras_iodev iodev;
+ struct cras_audio_format fmt;
+ unsigned int min_cb_level = 240;
+ int rc;
+ struct cras_rstream rstream1;
+ struct dev_stream stream1;
+ struct cras_iodev_info info;
+
+ ResetStubData();
+
+ rstream1.cb_threshold = min_cb_level;
+ stream1.stream = &rstream1;
+
+ memset(&iodev, 0, sizeof(iodev));
+
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+ iodev.ext_format = &fmt;
+ iodev.format = &fmt;
+ iodev.min_cb_level = min_cb_level;
+ iodev.get_buffer = get_buffer;
+ iodev.put_buffer = put_buffer;
+ iodev.frames_queued = frames_queued;
+ iodev.min_buffer_level = 0;
+ iodev.direction = CRAS_STREAM_OUTPUT;
+ iodev.buffer_size = BUFFER_SIZE;
+ iodev.no_stream = no_stream;
+ iodev.open_dev = open_dev;
+ iodev.start = fake_start;
+ iodev.info = info;
+ iodev_buffer_size = BUFFER_SIZE;
+
+ // Open device.
+ cras_iodev_open(&iodev, rstream1.cb_threshold);
+
+ // Add one stream to device.
+ cras_iodev_add_stream(&iodev, &stream1);
+
+ // Case 1: Assume device is not started yet.
+ iodev.state = CRAS_IODEV_STATE_OPEN;
+ // Assume sample is not ready yet.
+ dev_stream_playback_frames_ret = 0;
+
+ rc = cras_iodev_prepare_output_before_write_samples(&iodev);
+
+ EXPECT_EQ(0, rc);
+ // Device should remain in open state.
+ EXPECT_EQ(CRAS_IODEV_STATE_OPEN, iodev.state);
+ EXPECT_EQ(0, no_stream_called);
+
+ // Assume now sample is ready.
+ dev_stream_playback_frames_ret = 100;
+
+ rc = cras_iodev_prepare_output_before_write_samples(&iodev);
+
+ EXPECT_EQ(0, rc);
+ // Device should enter normal run state.
+ EXPECT_EQ(CRAS_IODEV_STATE_NORMAL_RUN, iodev.state);
+ EXPECT_EQ(0, no_stream_called);
+ // Need to fill 1 callback level of zeros;
+ EXPECT_EQ(min_cb_level, put_buffer_nframes);
+
+ ResetStubData();
+
+ // Case 2: Assume device is started and is in no stream state.
+ iodev.state = CRAS_IODEV_STATE_NO_STREAM_RUN;
+ // Sample is not ready yet.
+ dev_stream_playback_frames_ret = 0;
+
+ rc = cras_iodev_prepare_output_before_write_samples(&iodev);
+
+ EXPECT_EQ(0, rc);
+ // Device should remain in no_stream state.
+ EXPECT_EQ(CRAS_IODEV_STATE_NO_STREAM_RUN, iodev.state);
+ // Device in no_stream state should call no_stream ops once.
+ EXPECT_EQ(1, no_stream_called);
+ EXPECT_EQ(1, no_stream_enable);
+
+ // Assume now sample is ready.
+ dev_stream_playback_frames_ret = 100;
+
+ rc = cras_iodev_prepare_output_before_write_samples(&iodev);
+
+ EXPECT_EQ(0, rc);
+ // Device should enter normal run state.
+ EXPECT_EQ(CRAS_IODEV_STATE_NORMAL_RUN, iodev.state);
+ // Device should call no_stream ops with enable=0 to leave no stream state.
+ EXPECT_EQ(2, no_stream_called);
+ EXPECT_EQ(0, no_stream_enable);
+
+ ResetStubData();
+
+ // Case 3: Assume device is started and is in normal run state.
+ iodev.state = CRAS_IODEV_STATE_NORMAL_RUN;
+
+ rc = cras_iodev_prepare_output_before_write_samples(&iodev);
+
+ EXPECT_EQ(0, rc);
+ // Device should remain in normal run state.
+ EXPECT_EQ(CRAS_IODEV_STATE_NORMAL_RUN, iodev.state);
+ // Device in no_stream state should call no_stream ops once.
+ EXPECT_EQ(0, no_stream_called);
+
+ ResetStubData();
+
+ // Test for device with ramp. Device should start ramping
+ // when sample is ready.
+
+ // Assume device has ramp member.
+ iodev.ramp = reinterpret_cast<struct cras_ramp*>(0x1);
+
+ // Case 4.1: Assume device with ramp is started and is in no stream state.
+ iodev.state = CRAS_IODEV_STATE_NO_STREAM_RUN;
+ // Assume sample is ready.
+ dev_stream_playback_frames_ret = 100;
+
+ rc = cras_iodev_prepare_output_before_write_samples(&iodev);
+
+ // Device should start ramping up without setting mute callback.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, cras_ramp_start_is_called);
+ EXPECT_EQ(1, cras_ramp_start_is_up);
+ EXPECT_EQ(fmt.frame_rate * RAMP_NEW_STREAM_DURATION_SECS,
+ cras_ramp_start_duration_frames);
+ EXPECT_EQ(NULL, cras_ramp_start_cb);
+ EXPECT_EQ(NULL, cras_ramp_start_cb_data);
+
+ ResetStubData();
+
+ // Case 4.2: Assume device with ramp is started and is in no stream state.
+ // But system is muted.
+ iodev.state = CRAS_IODEV_STATE_NO_STREAM_RUN;
+ // Assume system is muted.
+ cras_system_get_mute_return = 1;
+ // Assume sample is ready.
+ dev_stream_playback_frames_ret = 100;
+
+ rc = cras_iodev_prepare_output_before_write_samples(&iodev);
+
+ // Device should not start ramping up because system is muted.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_ramp_start_is_called);
+
+ ResetStubData();
+
+ // Case 5.1: Assume device with ramp is in open state.
+ iodev.state = CRAS_IODEV_STATE_OPEN;
+ // Assume sample is ready.
+ dev_stream_playback_frames_ret = 100;
+
+ rc = cras_iodev_prepare_output_before_write_samples(&iodev);
+
+ // Device should start ramping up without setting mute callback.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, cras_ramp_start_is_called);
+ EXPECT_EQ(1, cras_ramp_start_is_up);
+ EXPECT_EQ(fmt.frame_rate * RAMP_NEW_STREAM_DURATION_SECS,
+ cras_ramp_start_duration_frames);
+ EXPECT_EQ(NULL, cras_ramp_start_cb);
+ EXPECT_EQ(NULL, cras_ramp_start_cb_data);
+
+ ResetStubData();
+
+ // Case 5.2: Assume device with ramp is in open state. But system is muted.
+ iodev.state = CRAS_IODEV_STATE_OPEN;
+ // Assume system is muted.
+ cras_system_get_mute_return = 1;
+ // Assume sample is ready.
+ dev_stream_playback_frames_ret = 100;
+
+ rc = cras_iodev_prepare_output_before_write_samples(&iodev);
+
+ // Device should not start ramping up because system is muted.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_ramp_start_is_called);
+}
+
+TEST(IoDev, StartRampUp) {
+ struct cras_iodev iodev;
+ int rc;
+ struct cras_audio_format fmt;
+ enum CRAS_IODEV_RAMP_REQUEST req;
+ memset(&iodev, 0, sizeof(iodev));
+
+ // Format will be used in cras_iodev_start_ramp to determine ramp duration.
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+ iodev.format = &fmt;
+
+ // Assume device has ramp member.
+ iodev.ramp = reinterpret_cast<struct cras_ramp*>(0x1);
+
+ // Case 1: Device is not opened yet.
+ ResetStubData();
+ iodev.state = CRAS_IODEV_STATE_CLOSE;
+ req = CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE;
+
+ rc = cras_iodev_start_ramp(&iodev, req);
+
+ // Ramp request is ignored.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_ramp_start_is_called);
+
+ // Case 2: Ramp up without mute.
+ ResetStubData();
+ iodev.state = CRAS_IODEV_STATE_OPEN;
+ req = CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK;
+
+ rc = cras_iodev_start_ramp(&iodev, req);
+
+ // Device should start ramping up without setting mute callback.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, cras_ramp_start_is_called);
+ EXPECT_EQ(1, cras_ramp_start_is_up);
+ EXPECT_EQ(fmt.frame_rate * RAMP_NEW_STREAM_DURATION_SECS,
+ cras_ramp_start_duration_frames);
+ EXPECT_EQ(NULL, cras_ramp_start_cb);
+ EXPECT_EQ(NULL, cras_ramp_start_cb_data);
+
+ // Case 3: Ramp up for unmute.
+ ResetStubData();
+ iodev.state = CRAS_IODEV_STATE_OPEN;
+ req = CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE;
+
+ rc = cras_iodev_start_ramp(&iodev, req);
+
+ // Device should start ramping up.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, cras_ramp_start_is_called);
+ EXPECT_EQ(1, cras_ramp_start_is_up);
+ EXPECT_EQ(fmt.frame_rate * RAMP_UNMUTE_DURATION_SECS,
+ cras_ramp_start_duration_frames);
+ // Callback for unmute is not used.
+ EXPECT_EQ(NULL, cras_ramp_start_cb);
+ // Device mute state is set after ramping starts.
+ EXPECT_EQ(1, cras_device_monitor_set_device_mute_state_called);
+ EXPECT_EQ(&iodev, cras_device_monitor_set_device_mute_state_dev);
+}
+
+TEST(IoDev, StartRampDown) {
+ struct cras_iodev iodev;
+ int rc;
+ struct cras_audio_format fmt;
+ enum CRAS_IODEV_RAMP_REQUEST req;
+ memset(&iodev, 0, sizeof(iodev));
+
+ // Format will be used in cras_iodev_start_ramp to determine ramp duration.
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+ iodev.format = &fmt;
+
+ // Assume device has ramp member.
+ iodev.ramp = reinterpret_cast<struct cras_ramp*>(0x1);
+
+ // Case 1: Device is not opened yet.
+ ResetStubData();
+ iodev.state = CRAS_IODEV_STATE_CLOSE;
+ req = CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE;
+
+ rc = cras_iodev_start_ramp(&iodev, req);
+
+ // Ramp request is ignored.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_ramp_start_is_called);
+
+ // Case 2: Ramp down for mute.
+ ResetStubData();
+ iodev.state = CRAS_IODEV_STATE_OPEN;
+ req = CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE;
+
+ rc = cras_iodev_start_ramp(&iodev, req);
+
+ // Device should start ramping down with mute callback.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, cras_ramp_start_is_called);
+ EXPECT_EQ(0, cras_ramp_start_is_up);
+ EXPECT_EQ(fmt.frame_rate * RAMP_MUTE_DURATION_SECS,
+ cras_ramp_start_duration_frames);
+
+ // Device mute state is not set yet. It should wait for ramp to finish.
+ EXPECT_EQ(0, cras_device_monitor_set_device_mute_state_called);
+ EXPECT_EQ(NULL, cras_device_monitor_set_device_mute_state_dev);
+
+ // Assume the callback is set, and it is later called after ramp is done.
+ // It should trigger cras_device_monitor_set_device_mute_state.
+ cras_ramp_start_cb(cras_ramp_start_cb_data);
+ EXPECT_EQ(1, cras_device_monitor_set_device_mute_state_called);
+ EXPECT_EQ(&iodev, cras_device_monitor_set_device_mute_state_dev);
+}
+
+TEST(IoDev, OutputDeviceShouldWake) {
+ struct cras_iodev iodev;
+ int rc;
+
+ memset(&iodev, 0, sizeof(iodev));
+
+ ResetStubData();
+
+ // Device is not running. No need to wake for this device.
+ iodev.state = CRAS_IODEV_STATE_OPEN;
+ rc = cras_iodev_odev_should_wake(&iodev);
+ EXPECT_EQ(0, rc);
+
+ // Device is running. Need to wake for this device.
+ iodev.state = CRAS_IODEV_STATE_NORMAL_RUN;
+ rc = cras_iodev_odev_should_wake(&iodev);
+ EXPECT_EQ(1, rc);
+
+ // Device is running. Device has output_should_wake ops.
+ iodev.output_should_wake = output_should_wake;
+ output_should_wake_ret = 0;
+ rc = cras_iodev_odev_should_wake(&iodev);
+ EXPECT_EQ(0, rc);
+
+ // Device is running. Device has output_should_wake ops.
+ output_should_wake_ret = 1;
+ rc = cras_iodev_odev_should_wake(&iodev);
+ EXPECT_EQ(1, rc);
+
+ // Ignore input device.
+ iodev.direction = CRAS_STREAM_INPUT;
+ rc = cras_iodev_odev_should_wake(&iodev);
+ EXPECT_EQ(0, rc);
+}
+
+TEST(IoDev, FramesToPlayInSleep) {
+ struct cras_iodev iodev;
+ unsigned int min_cb_level = 240, hw_level;
+ unsigned int got_hw_level, got_frames;
+ struct timespec hw_tstamp;
+
+ memset(&iodev, 0, sizeof(iodev));
+ iodev.frames_queued = frames_queued;
+ iodev.min_buffer_level = 0;
+ iodev.direction = CRAS_STREAM_OUTPUT;
+ iodev.buffer_size = BUFFER_SIZE;
+ iodev.min_cb_level = min_cb_level;
+
+ ResetStubData();
+
+ // Device is running. There is at least one stream for this device.
+ // hw_level is greater than min_cb_level.
+ iodev.state = CRAS_IODEV_STATE_NORMAL_RUN;
+ hw_level = min_cb_level + 50;
+ fr_queued = hw_level;
+ iodev.streams = reinterpret_cast<struct dev_stream *>(0x1);
+
+ got_frames = cras_iodev_frames_to_play_in_sleep(
+ &iodev, &got_hw_level, &hw_tstamp);
+ EXPECT_EQ(hw_level, got_hw_level);
+ EXPECT_EQ(hw_level, got_frames);
+
+ // Device is running. There is no stream for this device.
+ // hw_level is greater than min_cb_level.
+ iodev.streams = NULL;
+
+ got_frames = cras_iodev_frames_to_play_in_sleep(
+ &iodev, &got_hw_level, &hw_tstamp);
+ EXPECT_EQ(hw_level, got_hw_level);
+ EXPECT_EQ(hw_level - min_cb_level, got_frames);
+
+ // Device is running. There is no stream for this device.
+ // hw_level is less than min_cb_level.
+ iodev.streams = NULL;
+ hw_level = min_cb_level - 50;
+ fr_queued = hw_level;
+
+ got_frames = cras_iodev_frames_to_play_in_sleep(
+ &iodev, &got_hw_level, &hw_tstamp);
+ EXPECT_EQ(hw_level, got_hw_level);
+ EXPECT_EQ(0, got_frames);
+}
+
+static unsigned int get_num_underruns(const struct cras_iodev *iodev) {
+ return get_num_underruns_ret;
+}
+
+TEST(IoDev, GetNumUnderruns) {
+ struct cras_iodev iodev;
+ memset(&iodev, 0, sizeof(iodev));
+
+ EXPECT_EQ(0, cras_iodev_get_num_underruns(&iodev));
+
+ iodev.get_num_underruns = get_num_underruns;
+ get_num_underruns_ret = 10;
+ EXPECT_EQ(10, cras_iodev_get_num_underruns(&iodev));
+}
+
+TEST(IoDev, RequestReset) {
+ struct cras_iodev iodev;
+ memset(&iodev, 0, sizeof(iodev));
+
+ ResetStubData();
+
+ iodev.open_dev = open_dev;
+ iodev.direction = CRAS_STREAM_OUTPUT;
+
+ iodev.state = CRAS_IODEV_STATE_CLOSE;
+ iodev_buffer_size = 1024;
+
+ // Open device.
+ cras_iodev_open(&iodev, 240);
+
+ // The first reset request works.
+ EXPECT_EQ(0, cras_iodev_reset_request(&iodev));
+ EXPECT_EQ(1, device_monitor_reset_device_called);
+
+ // The second reset request will do nothing.
+ EXPECT_EQ(0, cras_iodev_reset_request(&iodev));
+ EXPECT_EQ(1, device_monitor_reset_device_called);
+
+ // Assume device is opened again.
+ cras_iodev_open(&iodev, 240);
+
+ // The reset request works.
+ EXPECT_EQ(0, cras_iodev_reset_request(&iodev));
+ EXPECT_EQ(2, device_monitor_reset_device_called);
+}
+
+static int output_underrun(struct cras_iodev *iodev) {
+ output_underrun_called++;
+ return 0;
+}
+
+
+TEST(IoDev, HandleOutputUnderrun) {
+ struct cras_iodev iodev;
+ struct cras_audio_format fmt;
+ unsigned int frames = 240;
+ int16_t *zeros;
+ int rc;
+
+ ResetStubData();
+
+ memset(&iodev, 0, sizeof(iodev));
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+ iodev.ext_format = &fmt;
+ iodev.get_buffer = get_buffer;
+ iodev.put_buffer = put_buffer;
+ iodev.direction = CRAS_STREAM_OUTPUT;
+ iodev.min_cb_level = frames;
+
+ // Default case, fill one block of zeros.
+ EXPECT_EQ(0, cras_iodev_output_underrun(&iodev));
+
+ EXPECT_EQ(frames, put_buffer_nframes);
+ zeros = (int16_t *)calloc(frames * 2, sizeof(*zeros));
+ rc = memcmp(audio_buffer, zeros, frames * 2 * 2);
+ free(zeros);
+ EXPECT_EQ(0, rc);
+
+ // Test iodev has output_underrun ops.
+ iodev.output_underrun = output_underrun;
+ EXPECT_EQ(0, cras_iodev_output_underrun(&iodev));
+ EXPECT_EQ(1, output_underrun_called);
+}
+
extern "C" {
// From libpthread.
@@ -824,6 +1969,19 @@ int pthread_join(pthread_t thread, void **value_ptr) {
return 0;
}
+// From audio_thread
+struct cras_fmt_conv *audio_thread_get_global_remix_converter()
+{
+ return NULL;
+}
+
+// Fromt fmt_conv
+void cras_channel_remix_convert(struct cras_fmt_conv *conv,
+ uint8_t *in_buf,
+ size_t frames)
+{
+}
+
// From buffer_share
struct buffer_share *buffer_share_create(unsigned int buf_sz) {
return NULL;
@@ -881,8 +2039,14 @@ void cras_dsp_load_pipeline(struct cras_dsp_context *ctx)
{
}
-void cras_dsp_set_variable(struct cras_dsp_context *ctx, const char *key,
- const char *value)
+void cras_dsp_set_variable_string(struct cras_dsp_context *ctx, const char *key,
+ const char *value)
+{
+}
+
+void cras_dsp_set_variable_boolean(struct cras_dsp_context *ctx,
+ const char *key,
+ char value)
{
}
@@ -968,7 +2132,8 @@ void cras_iodev_list_notify_nodes_changed()
notify_nodes_changed_called++;
}
-void cras_iodev_list_notify_active_node_changed()
+void cras_iodev_list_notify_active_node_changed(
+ enum CRAS_STREAM_DIRECTION direction)
{
notify_active_node_changed_called++;
}
@@ -1018,6 +2183,10 @@ size_t cras_system_get_volume() {
return cras_system_get_volume_return;
}
+long cras_system_get_capture_gain() {
+ return cras_system_get_capture_gain_ret_value;
+}
+
int cras_system_get_mute() {
return cras_system_get_mute_return;
}
@@ -1028,10 +2197,23 @@ int cras_system_get_capture_mute() {
void cras_scale_buffer(snd_pcm_format_t fmt, uint8_t *buffer,
unsigned int count, float scaler) {
+ cras_scale_buffer_called++;
cras_scale_buffer_fmt = fmt;
cras_scale_buffer_scaler = scaler;
}
+void cras_scale_buffer_increment(snd_pcm_format_t fmt, uint8_t *buff,
+ unsigned int frame, float scaler,
+ float increment, int channel)
+{
+ cras_scale_buffer_increment_fmt = fmt;
+ cras_scale_buffer_increment_buff = buff;
+ cras_scale_buffer_increment_frame = frame;
+ cras_scale_buffer_increment_scaler = scaler;
+ cras_scale_buffer_increment_increment = increment;
+ cras_scale_buffer_increment_channel = channel;
+}
+
size_t cras_mix_mute_buffer(uint8_t *dst,
size_t frame_bytes,
size_t count) {
@@ -1071,10 +2253,67 @@ unsigned int dev_stream_cb_threshold(const struct dev_stream *dev_stream) {
return 0;
}
+int dev_stream_attached_devs(const struct dev_stream *dev_stream) {
+ return 1;
+}
+
+void dev_stream_update_frames(const struct dev_stream *dev_stream) {
+}
+
+int dev_stream_playback_frames(const struct dev_stream *dev_stream) {
+ return dev_stream_playback_frames_ret;
+}
+
+int cras_device_monitor_reset_device(struct cras_iodev *iodev) {
+ device_monitor_reset_device_called++;
+ return 0;
+}
+
+void cras_ramp_destroy(struct cras_ramp* ramp) {
+ return;
+}
+
+int cras_ramp_start(struct cras_ramp *ramp, int is_up, int duration_frames,
+ cras_ramp_cb cb, void *cb_data)
+{
+ cras_ramp_start_is_called++;
+ cras_ramp_start_is_up = is_up;
+ cras_ramp_start_duration_frames = duration_frames;
+ cras_ramp_start_cb = cb;
+ cras_ramp_start_cb_data = cb_data;
+ return 0;
+}
+
+int cras_ramp_reset(struct cras_ramp *ramp) {
+ cras_ramp_reset_is_called++;
+ return 0;
+}
+
+struct cras_ramp_action cras_ramp_get_current_action(
+ const struct cras_ramp *ramp) {
+ return cras_ramp_get_current_action_ret;
+}
+
+int cras_ramp_update_ramped_frames(
+ struct cras_ramp *ramp, int num_frames) {
+ cras_ramp_update_ramped_frames_num_frames = num_frames;
+ return 0;
+}
+
+int cras_device_monitor_set_device_mute_state(struct cras_iodev *iodev)
+{
+ cras_device_monitor_set_device_mute_state_called++;
+ cras_device_monitor_set_device_mute_state_dev = iodev;
+ return 0;
+}
+
} // extern "C"
} // namespace
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
+ int rc = RUN_ALL_TESTS();
+
+ audio_thread_event_log_deinit(atlog);
+ return rc;
}
diff --git a/cras/src/tests/linear_resampler_unittest.cc b/cras/src/tests/linear_resampler_unittest.cc
index 6fb724e1..e2996484 100644
--- a/cras/src/tests/linear_resampler_unittest.cc
+++ b/cras/src/tests/linear_resampler_unittest.cc
@@ -192,15 +192,15 @@ TEST(LinearResampler, ResampleIntegerNoDstBuffer) {
extern "C" {
-void cras_mix_add_stride(int fmt, uint8_t *dst, uint8_t *src,
+void cras_mix_add_scale_stride(int fmt, uint8_t *dst, uint8_t *src,
unsigned int count, unsigned int dst_stride,
- unsigned int src_stride)
+ unsigned int src_stride, float scaler)
{
unsigned int i;
for (i = 0; i < count; i++) {
int32_t sum;
- sum = *(int16_t *)dst + *(int16_t *)src;
+ sum = *(int16_t *)dst + *(int16_t *)src * scaler;
if (sum > INT16_MAX)
sum = INT16_MAX;
else if (sum < INT16_MIN)
diff --git a/cras/src/tests/loopback_iodev_unittest.cc b/cras/src/tests/loopback_iodev_unittest.cc
index 02463a3a..b0387bfd 100644
--- a/cras/src/tests/loopback_iodev_unittest.cc
+++ b/cras/src/tests/loopback_iodev_unittest.cc
@@ -68,6 +68,7 @@ class LoopBackTestSuite : public testing::Test{
TEST_F(LoopBackTestSuite, InstallLoopHook) {
struct cras_iodev iodev;
+ struct timespec tstamp;
iodev.direction = CRAS_STREAM_OUTPUT;
iodev.format = &fmt_;
@@ -85,24 +86,19 @@ TEST_F(LoopBackTestSuite, InstallLoopHook) {
// Expect that a hook was added to the iodev
ASSERT_NE(reinterpret_cast<loopback_hook_t>(NULL), loop_hook);
- // Check device open status.
- EXPECT_EQ(1, loop_in_->is_open(loop_in_));
-
// Check zero frames queued.
- EXPECT_EQ(0, loop_in_->frames_queued(loop_in_));
+ EXPECT_EQ(0, loop_in_->frames_queued(loop_in_, &tstamp));
// Close loopback devices.
EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
EXPECT_EQ(reinterpret_cast<loopback_hook_t>(NULL), loop_hook);
-
- // Check device open status.
- EXPECT_EQ(0, loop_in_->is_open(loop_in_));
}
// Test how loopback works if there isn't any output devices open.
TEST_F(LoopBackTestSuite, OpenIdleSystem) {
cras_audio_area *area;
unsigned int nread = 1024;
+ struct timespec tstamp;
int rc;
// No active output device.
@@ -115,7 +111,7 @@ TEST_F(LoopBackTestSuite, OpenIdleSystem) {
// Should be 480 samples after 480/frame rate seconds
time_now.tv_nsec += 480 * 1e9 / 48000;
- EXPECT_EQ(480, loop_in_->frames_queued(loop_in_));
+ EXPECT_EQ(480, loop_in_->frames_queued(loop_in_, &tstamp));
// Verify frames from loopback record.
loop_in_->get_buffer(loop_in_, &area, &nread);
@@ -126,7 +122,7 @@ TEST_F(LoopBackTestSuite, OpenIdleSystem) {
loop_in_->put_buffer(loop_in_, nread);
// Check zero frames queued.
- EXPECT_EQ(0, loop_in_->frames_queued(loop_in_));
+ EXPECT_EQ(0, loop_in_->frames_queued(loop_in_, &tstamp));
EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
}
@@ -138,6 +134,7 @@ TEST_F(LoopBackTestSuite, SimpleLoopback) {
int rc;
struct cras_iodev iodev;
struct dev_stream stream;
+ struct timespec tstamp;
iodev.streams = &stream;
enabled_dev = &iodev;
@@ -156,7 +153,7 @@ TEST_F(LoopBackTestSuite, SimpleLoopback) {
loop_in_->put_buffer(loop_in_, nread);
// Check zero frames queued.
- EXPECT_EQ(0, loop_in_->frames_queued(loop_in_));
+ EXPECT_EQ(0, loop_in_->frames_queued(loop_in_, &tstamp));
EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
}
diff --git a/cras/src/tests/mix_unittest.cc b/cras/src/tests/mix_unittest.cc
index 401619a2..df4c5b4f 100644
--- a/cras/src/tests/mix_unittest.cc
+++ b/cras/src/tests/mix_unittest.cc
@@ -18,6 +18,11 @@ static const size_t kBufferFrames = 8192;
static const size_t kNumChannels = 2;
static const size_t kNumSamples = kBufferFrames * kNumChannels;
+
+static inline int need_to_scale(float scaler) {
+ return (scaler < 0.99 || scaler > 1.01);
+}
+
class MixTestSuiteS16_LE : public testing::Test{
protected:
virtual void SetUp() {
@@ -40,6 +45,55 @@ class MixTestSuiteS16_LE : public testing::Test{
free(src_buffer_);
}
+ void _SetupBuffer() {
+ for (size_t i = 0; i < kBufferFrames; i++) {
+ src_buffer_[i] = i + (INT16_MAX >> 2);
+ mix_buffer_[i] = i + (INT16_MAX >> 2);
+ compare_buffer_[i] = mix_buffer_[i];
+ }
+ for (size_t i = kBufferFrames; i < kBufferFrames * 2; i++) {
+ src_buffer_[i] = i - (INT16_MAX >> 2);
+ mix_buffer_[i] = i - (INT16_MAX >> 2);
+ compare_buffer_[i] = mix_buffer_[i];
+ }
+ }
+
+ void TestScaleStride(float scaler) {
+ _SetupBuffer();
+ for (size_t i = 0; i < kBufferFrames * 2; i += 2) {
+ int32_t tmp;
+ if (need_to_scale(scaler))
+ tmp = mix_buffer_[i] + src_buffer_[i/2] * scaler;
+ else
+ tmp = mix_buffer_[i] + src_buffer_[i/2];
+ if (tmp > INT16_MAX)
+ tmp = INT16_MAX;
+ else if (tmp < INT16_MIN)
+ tmp = INT16_MIN;
+ compare_buffer_[i] = tmp;
+ }
+
+ cras_mix_add_scale_stride(
+ fmt_, (uint8_t *)mix_buffer_, (uint8_t *)src_buffer_,
+ kBufferFrames, 4, 2, scaler);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 4));
+ }
+
+ void ScaleIncrement(float start_scaler, float increment) {
+ float scaler = start_scaler;
+ for (size_t i = 0; i < kBufferFrames * 2; i++) {
+ if (scaler > 0.9999999) {
+ } else if (scaler < 0.0000001) {
+ compare_buffer_[i] = 0;
+ } else {
+ compare_buffer_[i] = mix_buffer_[i] * scaler;
+ }
+ if (i % 2 == 1)
+ scaler += increment;
+ }
+ }
+
int16_t *mix_buffer_;
int16_t *src_buffer_;
int16_t *compare_buffer_;
@@ -114,6 +168,89 @@ TEST_F(MixTestSuiteS16_LE, MixTwoSecondHalfVolume) {
EXPECT_EQ(0, memcmp(mix_buffer_, compare_buffer_, kBufferFrames*4));
}
+TEST_F(MixTestSuiteS16_LE, ScaleFullVolumeIncrement) {
+ float increment = 0.01;
+ int step = 2;
+ float start_scaler = 0.999999999;
+
+ _SetupBuffer();
+ // Scale full volume with positive increment will not change buffer.
+ memcpy(compare_buffer_, src_buffer_, kBufferFrames * 4);
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 4));
+}
+
+TEST_F(MixTestSuiteS16_LE, ScaleMinVolumeIncrement) {
+ float increment = -0.01;
+ int step = 2;
+ float start_scaler = 0.000000001;
+
+ _SetupBuffer();
+ // Scale min volume with negative increment will change buffer to zeros.
+ memset(compare_buffer_, 0, kBufferFrames * 4);
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 4));
+}
+
+TEST_F(MixTestSuiteS16_LE, ScaleVolumePositiveIncrement) {
+ float increment = 0.0001;
+ int step = 2;
+ float start_scaler = 0.1;
+
+ _SetupBuffer();
+ ScaleIncrement(start_scaler, increment);
+
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 4));
+}
+
+TEST_F(MixTestSuiteS16_LE, ScaleVolumeNegativeIncrement) {
+ float increment = -0.0001;
+ int step = 2;
+ float start_scaler = 0.8;
+
+ _SetupBuffer();
+ ScaleIncrement(start_scaler, increment);
+
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 4));
+}
+
+TEST_F(MixTestSuiteS16_LE, ScaleVolumeStartFullNegativeIncrement) {
+ float increment = -0.0001;
+ int step = 2;
+ float start_scaler = 1.0;
+
+ _SetupBuffer();
+ ScaleIncrement(start_scaler, increment);
+
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 4));
+}
+
+TEST_F(MixTestSuiteS16_LE, ScaleVolumeStartZeroPositiveIncrement) {
+ float increment = 0.0001;
+ int step = 2;
+ float start_scaler = 0.0;
+
+ _SetupBuffer();
+ ScaleIncrement(start_scaler, increment);
+
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 4));
+}
+
TEST_F(MixTestSuiteS16_LE, ScaleFullVolume) {
memcpy(compare_buffer_, src_buffer_, kBufferFrames * 4);
cras_scale_buffer(fmt_, (uint8_t *)mix_buffer_, kNumSamples, 0.999999999);
@@ -136,16 +273,12 @@ TEST_F(MixTestSuiteS16_LE, ScaleHalfVolume) {
EXPECT_EQ(0, memcmp(compare_buffer_, src_buffer_, kBufferFrames * 4));
}
-TEST_F(MixTestSuiteS16_LE, StrideCopy) {
- for (size_t i = 0; i < kBufferFrames * 2; i += 2)
- compare_buffer_[i] = src_buffer_[i/2];
- for (size_t i = 1; i < kBufferFrames * 2; i += 2)
- compare_buffer_[i] = 0;
- memset(mix_buffer_, 0, kBufferFrames * 4);
- cras_mix_add_stride(fmt_, (uint8_t *)mix_buffer_, (uint8_t *)src_buffer_,
- kBufferFrames, 4, 2);
- EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 4));
+
+TEST_F(MixTestSuiteS16_LE, StrideCopy) {
+ TestScaleStride(1.0);
+ TestScaleStride(100);
+ TestScaleStride(0.5);
}
class MixTestSuiteS24_LE : public testing::Test{
@@ -171,6 +304,55 @@ class MixTestSuiteS24_LE : public testing::Test{
free(src_buffer_);
}
+ void _SetupBuffer() {
+ for (size_t i = 0; i < kBufferFrames; i++) {
+ src_buffer_[i] = i + (0x007fffff >> 2);
+ mix_buffer_[i] = i + (0x007fffff >> 2);
+ compare_buffer_[i] = mix_buffer_[i];
+ }
+ for (size_t i = kBufferFrames; i < kBufferFrames * 2; i++) {
+ src_buffer_[i] = i - (0x007fffff >> 2);
+ mix_buffer_[i] = i - (0x007fffff >> 2);
+ compare_buffer_[i] = mix_buffer_[i];
+ }
+ }
+
+ void TestScaleStride(float scaler) {
+ _SetupBuffer();
+ for (size_t i = 0; i < kBufferFrames * 2; i += 2) {
+ int32_t tmp;
+ if (need_to_scale(scaler))
+ tmp = mix_buffer_[i] + src_buffer_[i/2] * scaler;
+ else
+ tmp = mix_buffer_[i] + src_buffer_[i/2];
+ if (tmp > 0x007fffff)
+ tmp = 0x007fffff;
+ else if (tmp < (int32_t)0xff800000)
+ tmp = (int32_t)0xff800000;
+ compare_buffer_[i] = tmp;
+ }
+
+ cras_mix_add_scale_stride(
+ fmt_, (uint8_t *)mix_buffer_, (uint8_t *)src_buffer_,
+ kBufferFrames, 8, 4, scaler);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 8));
+ }
+
+ void ScaleIncrement(float start_scaler, float increment) {
+ float scaler = start_scaler;
+ for (size_t i = 0; i < kBufferFrames * 2; i++) {
+ if (scaler > 0.9999999) {
+ } else if (scaler < 0.0000001) {
+ compare_buffer_[i] = 0;
+ } else {
+ compare_buffer_[i] = mix_buffer_[i] * scaler;
+ }
+ if (i % 2 == 1)
+ scaler += increment;
+ }
+ }
+
int32_t *mix_buffer_;
int32_t *src_buffer_;
int32_t *compare_buffer_;
@@ -246,6 +428,89 @@ TEST_F(MixTestSuiteS24_LE, MixTwoSecondHalfVolume) {
EXPECT_EQ(0, memcmp(mix_buffer_, compare_buffer_, kBufferFrames * fr_bytes_));
}
+TEST_F(MixTestSuiteS24_LE, ScaleFullVolumeIncrement) {
+ float increment = 0.01;
+ int step = 2;
+ float start_scaler = 0.999999999;
+
+ _SetupBuffer();
+ // Scale full volume with positive increment will not change buffer.
+ memcpy(compare_buffer_, src_buffer_, kBufferFrames * fr_bytes_);
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * fr_bytes_));
+}
+
+TEST_F(MixTestSuiteS24_LE, ScaleMinVolumeIncrement) {
+ float increment = -0.01;
+ int step = 2;
+ float start_scaler = 0.000000001;
+
+ _SetupBuffer();
+ // Scale min volume with negative increment will change buffer to zeros.
+ memset(compare_buffer_, 0, kBufferFrames * fr_bytes_);
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * fr_bytes_));
+}
+
+TEST_F(MixTestSuiteS24_LE, ScaleVolumePositiveIncrement) {
+ float increment = 0.0001;
+ int step = 2;
+ float start_scaler = 0.1;
+
+ _SetupBuffer();
+ ScaleIncrement(start_scaler, increment);
+
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * fr_bytes_));
+}
+
+TEST_F(MixTestSuiteS24_LE, ScaleVolumeNegativeIncrement) {
+ float increment = -0.0001;
+ int step = 2;
+ float start_scaler = 0.8;
+
+ _SetupBuffer();
+ ScaleIncrement(start_scaler, increment);
+
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * fr_bytes_));
+}
+
+TEST_F(MixTestSuiteS24_LE, ScaleVolumeStartFullNegativeIncrement) {
+ float increment = -0.0001;
+ int step = 2;
+ float start_scaler = 1.0;
+
+ _SetupBuffer();
+ ScaleIncrement(start_scaler, increment);
+
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 4));
+}
+
+TEST_F(MixTestSuiteS24_LE, ScaleVolumeStartZeroPositiveIncrement) {
+ float increment = 0.0001;
+ int step = 2;
+ float start_scaler = 0.0;
+
+ _SetupBuffer();
+ ScaleIncrement(start_scaler, increment);
+
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 4));
+}
+
TEST_F(MixTestSuiteS24_LE, ScaleFullVolume) {
memcpy(compare_buffer_, src_buffer_, kBufferFrames * fr_bytes_);
cras_scale_buffer(fmt_, (uint8_t *)mix_buffer_, kNumSamples, 0.999999999);
@@ -269,15 +534,9 @@ TEST_F(MixTestSuiteS24_LE, ScaleHalfVolume) {
}
TEST_F(MixTestSuiteS24_LE, StrideCopy) {
- for (size_t i = 0; i < kBufferFrames * 2; i += 2)
- compare_buffer_[i] = src_buffer_[i/2];
- for (size_t i = 1; i < kBufferFrames * 2; i += 2)
- compare_buffer_[i] = 0;
- memset(mix_buffer_, 0, kBufferFrames * 8);
- cras_mix_add_stride(fmt_, (uint8_t *)mix_buffer_, (uint8_t *)src_buffer_,
- kBufferFrames, 8, 4);
-
- EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 8));
+ TestScaleStride(1.0);
+ TestScaleStride(100);
+ TestScaleStride(0.1);
}
class MixTestSuiteS32_LE : public testing::Test{
@@ -303,6 +562,55 @@ class MixTestSuiteS32_LE : public testing::Test{
free(src_buffer_);
}
+ void _SetupBuffer() {
+ for (size_t i = 0; i < kBufferFrames; i++) {
+ src_buffer_[i] = i + (INT32_MAX >> 2);
+ mix_buffer_[i] = i + (INT32_MAX >> 2);
+ compare_buffer_[i] = mix_buffer_[i];
+ }
+ for (size_t i = kBufferFrames; i < kBufferFrames * 2; i++) {
+ src_buffer_[i] = i - (INT32_MAX >> 2);
+ mix_buffer_[i] = i - (INT32_MAX >> 2);
+ compare_buffer_[i] = mix_buffer_[i];
+ }
+ }
+
+ void TestScaleStride(float scaler) {
+ _SetupBuffer();
+ for (size_t i = 0; i < kBufferFrames * 2; i += 2) {
+ int64_t tmp;
+ if (need_to_scale(scaler))
+ tmp = mix_buffer_[i] + src_buffer_[i/2] * scaler;
+ else
+ tmp = mix_buffer_[i] + src_buffer_[i/2];
+ if (tmp > INT32_MAX)
+ tmp = INT32_MAX;
+ else if (tmp < INT32_MIN)
+ tmp = INT32_MIN;
+ compare_buffer_[i] = tmp;
+ }
+
+ cras_mix_add_scale_stride(
+ fmt_, (uint8_t *)mix_buffer_, (uint8_t *)src_buffer_,
+ kBufferFrames, 8, 4, scaler);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 8));
+ }
+
+ void ScaleIncrement(float start_scaler, float increment) {
+ float scaler = start_scaler;
+ for (size_t i = 0; i < kBufferFrames * 2; i++) {
+ if (scaler > 0.9999999) {
+ } else if (scaler < 0.0000001) {
+ compare_buffer_[i] = 0;
+ } else {
+ compare_buffer_[i] = mix_buffer_[i] * scaler;
+ }
+ if (i % 2 == 1)
+ scaler += increment;
+ }
+ }
+
int32_t *mix_buffer_;
int32_t *src_buffer_;
int32_t *compare_buffer_;
@@ -378,6 +686,89 @@ TEST_F(MixTestSuiteS32_LE, MixTwoSecondHalfVolume) {
EXPECT_EQ(0, memcmp(mix_buffer_, compare_buffer_, kBufferFrames * fr_bytes_));
}
+TEST_F(MixTestSuiteS32_LE, ScaleFullVolumeIncrement) {
+ float increment = 0.01;
+ int step = 2;
+ float start_scaler = 0.999999999;
+
+ _SetupBuffer();
+ // Scale full volume with positive increment will not change buffer.
+ memcpy(compare_buffer_, src_buffer_, kBufferFrames * fr_bytes_);
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * fr_bytes_));
+}
+
+TEST_F(MixTestSuiteS32_LE, ScaleMinVolumeIncrement) {
+ float increment = -0.01;
+ int step = 2;
+ float start_scaler = 0.000000001;
+
+ _SetupBuffer();
+ // Scale min volume with negative increment will change buffer to zeros.
+ memset(compare_buffer_, 0, kBufferFrames * fr_bytes_);
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * fr_bytes_));
+}
+
+TEST_F(MixTestSuiteS32_LE, ScaleVolumePositiveIncrement) {
+ float increment = 0.0001;
+ int step = 2;
+ float start_scaler = 0.1;
+
+ _SetupBuffer();
+ ScaleIncrement(start_scaler, increment);
+
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * fr_bytes_));
+}
+
+TEST_F(MixTestSuiteS32_LE, ScaleVolumeNegativeIncrement) {
+ float increment = -0.0001;
+ int step = 2;
+ float start_scaler = 0.8;
+
+ _SetupBuffer();
+ ScaleIncrement(start_scaler, increment);
+
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * fr_bytes_));
+}
+
+TEST_F(MixTestSuiteS32_LE, ScaleVolumeStartFullNegativeIncrement) {
+ float increment = -0.0001;
+ int step = 2;
+ float start_scaler = 1.0;
+
+ _SetupBuffer();
+ ScaleIncrement(start_scaler, increment);
+
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 4));
+}
+
+TEST_F(MixTestSuiteS32_LE, ScaleVolumeStartZeroPositiveIncrement) {
+ float increment = 0.0001;
+ int step = 2;
+ float start_scaler = 0.0;
+
+ _SetupBuffer();
+ ScaleIncrement(start_scaler, increment);
+
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 4));
+}
+
TEST_F(MixTestSuiteS32_LE, ScaleFullVolume) {
memcpy(compare_buffer_, src_buffer_, kBufferFrames * fr_bytes_);
cras_scale_buffer(fmt_, (uint8_t *)mix_buffer_, kNumSamples, 0.999999999);
@@ -401,15 +792,9 @@ TEST_F(MixTestSuiteS32_LE, ScaleHalfVolume) {
}
TEST_F(MixTestSuiteS32_LE, StrideCopy) {
- for (size_t i = 0; i < kBufferFrames * 2; i += 2)
- compare_buffer_[i] = src_buffer_[i/2];
- for (size_t i = 1; i < kBufferFrames * 2; i += 2)
- compare_buffer_[i] = 0;
- memset(mix_buffer_, 0, kBufferFrames * 8);
- cras_mix_add_stride(fmt_, (uint8_t *)mix_buffer_, (uint8_t *)src_buffer_,
- kBufferFrames, 8, 4);
-
- EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 8));
+ TestScaleStride(1.0);
+ TestScaleStride(100);
+ TestScaleStride(0.1);
}
class MixTestSuiteS24_3LE : public testing::Test{
@@ -436,6 +821,69 @@ class MixTestSuiteS24_3LE : public testing::Test{
free(src_buffer_);
}
+ void _SetupBuffer() {
+ memset(compare_buffer_, 0, kBufferFrames * fr_bytes_);
+ for (size_t i = 0; i < kBufferFrames; i++) {
+ int32_t tmp = (i << 8) + (INT32_MAX >> 2);
+ memcpy(src_buffer_ + 3*i, (uint8_t *)&tmp + 1, 3);
+ memcpy(mix_buffer_ + 3*i, (uint8_t *)&tmp + 1, 3);
+ memcpy(compare_buffer_ + 3*i, (uint8_t *)&tmp + 1, 3);
+ }
+ for (size_t i = kBufferFrames; i < kBufferFrames * 2; i++) {
+ int32_t tmp = (i << 8) - (INT32_MAX >> 2);
+ memcpy(src_buffer_ + 3*i, (uint8_t *)&tmp + 1, 3);
+ memcpy(mix_buffer_ + 3*i, (uint8_t *)&tmp + 1, 3);
+ memcpy(compare_buffer_ + 3*i, (uint8_t *)&tmp + 1, 3);
+ }
+ }
+
+ void TestScaleStride(float scaler) {
+ _SetupBuffer();
+ for (size_t i = 0; i < kBufferFrames * kNumChannels; i += 2) {
+ int64_t tmp;
+ int32_t src_frame = 0;
+ int32_t dst_frame = 0;
+ memcpy((uint8_t *)&src_frame + 1, src_buffer_ + 3*i/2, 3);
+ memcpy((uint8_t *)&dst_frame + 1, mix_buffer_ + 3*i, 3);
+ if (need_to_scale(scaler))
+ tmp = (int64_t)dst_frame + (int64_t)src_frame * scaler;
+ else
+ tmp = (int64_t)dst_frame + (int64_t)src_frame;
+ if (tmp > INT32_MAX)
+ tmp = INT32_MAX;
+ else if (tmp < INT32_MIN)
+ tmp = INT32_MIN;
+ dst_frame = (int32_t)tmp;
+ memcpy(compare_buffer_ + 3*i, (uint8_t *)&dst_frame + 1, 3);
+ }
+
+ cras_mix_add_scale_stride(
+ fmt_, (uint8_t *)mix_buffer_, (uint8_t *)src_buffer_,
+ kBufferFrames, 6, 3, scaler);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 6));
+ }
+
+ void ScaleIncrement(float start_scaler, float increment) {
+ float scaler = start_scaler;
+ for (size_t i = 0; i < kBufferFrames * kNumChannels; i++) {
+ int32_t tmp = 0;
+ memcpy((uint8_t *)&tmp + 1, src_buffer_ + 3*i, 3);
+
+ if (scaler > 0.9999999) {
+ } else if (scaler < 0.0000001) {
+ tmp = 0;
+ } else {
+ tmp *= scaler;
+ }
+
+ memcpy(compare_buffer_ + 3*i, (uint8_t *)&tmp + 1, 3);
+
+ if (i % 2 == 1)
+ scaler += increment;
+ }
+ }
+
uint8_t *mix_buffer_;
uint8_t *src_buffer_;
uint8_t *compare_buffer_;
@@ -528,6 +976,89 @@ TEST_F(MixTestSuiteS24_3LE, MixTwoSecondHalfVolume) {
EXPECT_EQ(0, memcmp(mix_buffer_, compare_buffer_, kBufferFrames * fr_bytes_));
}
+TEST_F(MixTestSuiteS24_3LE, ScaleFullVolumeIncrement) {
+ float increment = 0.01;
+ int step = 2;
+ float start_scaler = 0.999999999;
+
+ _SetupBuffer();
+ // Scale full volume with positive increment will not change buffer.
+ memcpy(compare_buffer_, src_buffer_, kBufferFrames * fr_bytes_);
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * fr_bytes_));
+}
+
+TEST_F(MixTestSuiteS24_3LE, ScaleMinVolumeIncrement) {
+ float increment = -0.01;
+ int step = 2;
+ float start_scaler = 0.000000001;
+
+ _SetupBuffer();
+ // Scale min volume with negative increment will change buffer to zeros.
+ memset(compare_buffer_, 0, kBufferFrames * fr_bytes_);
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * fr_bytes_));
+}
+
+TEST_F(MixTestSuiteS24_3LE, ScaleVolumePositiveIncrement) {
+ float increment = 0.0001;
+ int step = 2;
+ float start_scaler = 0.1;
+
+ _SetupBuffer();
+ ScaleIncrement(start_scaler, increment);
+
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * fr_bytes_));
+}
+
+TEST_F(MixTestSuiteS24_3LE, ScaleVolumeNegativeIncrement) {
+ float increment = -0.0001;
+ int step = 2;
+ float start_scaler = 0.8;
+
+ _SetupBuffer();
+ ScaleIncrement(start_scaler, increment);
+
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * fr_bytes_));
+}
+
+TEST_F(MixTestSuiteS24_3LE, ScaleVolumeStartFullNegativeIncrement) {
+ float increment = -0.0001;
+ int step = 2;
+ float start_scaler = 1.0;
+
+ _SetupBuffer();
+ ScaleIncrement(start_scaler, increment);
+
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 4));
+}
+
+TEST_F(MixTestSuiteS24_3LE, ScaleVolumeStartZeroPositiveIncrement) {
+ float increment = 0.0001;
+ int step = 2;
+ float start_scaler = 0.0;
+
+ _SetupBuffer();
+ ScaleIncrement(start_scaler, increment);
+
+ cras_scale_buffer_increment(
+ fmt_, (uint8_t *)mix_buffer_, kBufferFrames, start_scaler, increment, step);
+
+ EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 4));
+}
+
TEST_F(MixTestSuiteS24_3LE, ScaleFullVolume) {
memcpy(compare_buffer_, src_buffer_, kBufferFrames * fr_bytes_);
cras_scale_buffer(fmt_, (uint8_t *)mix_buffer_, kNumSamples, 0.999999999);
@@ -555,15 +1086,9 @@ TEST_F(MixTestSuiteS24_3LE, ScaleHalfVolume) {
}
TEST_F(MixTestSuiteS24_3LE, StrideCopy) {
- for (size_t i = 0; i < kBufferFrames * kNumChannels; i += 2)
- memcpy(compare_buffer_ + 3*i, src_buffer_ + 3*i/2, 3);
- for (size_t i = 1; i < kBufferFrames * kNumChannels; i += 2)
- memset(compare_buffer_ + 3*i, 0, 3);
- memset(mix_buffer_, 0, kBufferFrames * 6);
- cras_mix_add_stride(fmt_, (uint8_t *)mix_buffer_, (uint8_t *)src_buffer_,
- kBufferFrames, 6, 3);
-
- EXPECT_EQ(0, memcmp(compare_buffer_, mix_buffer_, kBufferFrames * 6));
+ TestScaleStride(1.0);
+ TestScaleStride(100);
+ TestScaleStride(0.1);
}
/* Stubs */
diff --git a/cras/src/tests/observer_unittest.cc b/cras/src/tests/observer_unittest.cc
new file mode 100644
index 00000000..d45a6a4a
--- /dev/null
+++ b/cras/src/tests/observer_unittest.cc
@@ -0,0 +1,625 @@
+// Copyright 2016 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <syslog.h>
+
+#include <vector>
+#include <map>
+
+extern "C" {
+#include "cras_observer.c"
+}
+
+namespace {
+
+static size_t cras_alert_destroy_called;
+static size_t cras_alert_create_called;
+static std::vector<struct cras_alert *> cras_alert_create_return_values;
+typedef std::map<struct cras_alert *, void *> alert_callback_map;
+static alert_callback_map cras_alert_create_prepare_map;
+static alert_callback_map cras_alert_add_callback_map;
+typedef std::map<struct cras_alert *, unsigned int> alert_flags_map;
+static alert_flags_map cras_alert_create_flags_map;
+static struct cras_alert *cras_alert_pending_alert_value;
+static void *cras_alert_pending_data_value = NULL;
+static size_t cras_alert_pending_data_size_value;
+static size_t cras_iodev_list_update_device_list_called;
+static std::vector<void *> cb_context;
+static size_t cb_output_volume_changed_called;
+static std::vector<int32_t> cb_output_volume_changed_volume;
+static size_t cb_output_mute_changed_called;
+static std::vector<int> cb_output_mute_changed_muted;
+static std::vector<int> cb_output_mute_changed_user_muted;
+static std::vector<int> cb_output_mute_changed_mute_locked;
+static size_t cb_capture_gain_changed_called;
+static std::vector<int32_t> cb_capture_gain_changed_gain;
+static size_t cb_capture_mute_changed_called;
+static std::vector<int> cb_capture_mute_changed_muted;
+static std::vector<int> cb_capture_mute_changed_mute_locked;
+static size_t cb_nodes_changed_called;
+static size_t cb_active_node_changed_called;
+static std::vector<enum CRAS_STREAM_DIRECTION> cb_active_node_changed_dir;
+static std::vector<cras_node_id_t> cb_active_node_changed_node_id;
+static size_t cb_output_node_volume_changed_called;
+static std::vector<cras_node_id_t> cb_output_node_volume_changed_node_id;
+static std::vector<int32_t> cb_output_node_volume_changed_volume;
+static size_t cb_node_left_right_swapped_changed_called;
+static std::vector<cras_node_id_t> cb_node_left_right_swapped_changed_node_id;
+static std::vector<int> cb_node_left_right_swapped_changed_swapped;
+static size_t cb_input_node_gain_changed_called;
+static std::vector<cras_node_id_t> cb_input_node_gain_changed_node_id;
+static std::vector<int32_t> cb_input_node_gain_changed_gain;
+static size_t cb_num_active_streams_changed_called;
+static std::vector<enum CRAS_STREAM_DIRECTION>
+ cb_num_active_streams_changed_dir;
+static std::vector<uint32_t> cb_num_active_streams_changed_num;
+
+static void ResetStubData() {
+ cras_alert_destroy_called = 0;
+ cras_alert_create_called = 0;
+ cras_alert_create_return_values.clear();
+ cras_alert_create_prepare_map.clear();
+ cras_alert_create_flags_map.clear();
+ cras_alert_add_callback_map.clear();
+ cras_alert_pending_alert_value = NULL;
+ cras_alert_pending_data_size_value = 0;
+ if (cras_alert_pending_data_value) {
+ free(cras_alert_pending_data_value);
+ cras_alert_pending_data_value = NULL;
+ }
+ cras_iodev_list_update_device_list_called = 0;
+ cb_context.clear();
+ cb_output_volume_changed_called = 0;
+ cb_output_volume_changed_volume.clear();
+ cb_output_mute_changed_called = 0;
+ cb_output_mute_changed_muted.clear();
+ cb_output_mute_changed_user_muted.clear();
+ cb_output_mute_changed_mute_locked.clear();
+ cb_capture_gain_changed_called = 0;
+ cb_capture_gain_changed_gain.clear();
+ cb_capture_mute_changed_called = 0;
+ cb_capture_mute_changed_muted.clear();
+ cb_capture_mute_changed_mute_locked.clear();
+ cb_nodes_changed_called = 0;
+ cb_active_node_changed_called = 0;
+ cb_active_node_changed_dir.clear();
+ cb_active_node_changed_node_id.clear();
+ cb_output_node_volume_changed_called = 0;
+ cb_output_node_volume_changed_node_id.clear();
+ cb_output_node_volume_changed_volume.clear();
+ cb_node_left_right_swapped_changed_called = 0;
+ cb_node_left_right_swapped_changed_node_id.clear();
+ cb_node_left_right_swapped_changed_swapped.clear();
+ cb_input_node_gain_changed_called = 0;
+ cb_input_node_gain_changed_node_id.clear();
+ cb_input_node_gain_changed_gain.clear();
+ cb_num_active_streams_changed_called = 0;
+ cb_num_active_streams_changed_dir.clear();
+ cb_num_active_streams_changed_num.clear();
+}
+
+/* System output volume changed. */
+void cb_output_volume_changed(void *context, int32_t volume) {
+ cb_output_volume_changed_called++;
+ cb_context.push_back(context);
+ cb_output_volume_changed_volume.push_back(volume);
+}
+/* System output mute changed. */
+void cb_output_mute_changed(void *context,
+ int muted, int user_muted, int mute_locked) {
+ cb_output_mute_changed_called++;
+ cb_context.push_back(context);
+ cb_output_mute_changed_muted.push_back(muted);
+ cb_output_mute_changed_user_muted.push_back(user_muted);
+ cb_output_mute_changed_mute_locked.push_back(mute_locked);
+}
+/* System input/capture gain changed. */
+void cb_capture_gain_changed(void *context, int32_t gain) {
+ cb_capture_gain_changed_called++;
+ cb_context.push_back(context);
+ cb_capture_gain_changed_gain.push_back(gain);
+}
+
+/* System input/capture mute changed. */
+void cb_capture_mute_changed(void *context, int muted, int mute_locked) {
+ cb_capture_mute_changed_called++;
+ cb_context.push_back(context);
+ cb_capture_mute_changed_muted.push_back(muted);
+ cb_capture_mute_changed_mute_locked.push_back(mute_locked);
+}
+
+/* Device or node topology changed. */
+void cb_nodes_changed(void *context) {
+ cb_nodes_changed_called++;
+ cb_context.push_back(context);
+}
+
+/* Active node changed. A notification is sent for every change.
+ * When there is no active node, node_id is 0. */
+void cb_active_node_changed(void *context,
+ enum CRAS_STREAM_DIRECTION dir,
+ cras_node_id_t node_id) {
+ cb_active_node_changed_called++;
+ cb_context.push_back(context);
+ cb_active_node_changed_dir.push_back(dir);
+ cb_active_node_changed_node_id.push_back(node_id);
+}
+
+/* Output node volume changed. */
+void cb_output_node_volume_changed(void *context,
+ cras_node_id_t node_id,
+ int32_t volume) {
+ cb_output_node_volume_changed_called++;
+ cb_context.push_back(context);
+ cb_output_node_volume_changed_node_id.push_back(node_id);
+ cb_output_node_volume_changed_volume.push_back(volume);
+}
+
+/* Node left/right swapped state change. */
+void cb_node_left_right_swapped_changed(void *context,
+ cras_node_id_t node_id,
+ int swapped) {
+ cb_node_left_right_swapped_changed_called++;
+ cb_context.push_back(context);
+ cb_node_left_right_swapped_changed_node_id.push_back(node_id);
+ cb_node_left_right_swapped_changed_swapped.push_back(swapped);
+}
+
+/* Input gain changed. */
+void cb_input_node_gain_changed(void *context,
+ cras_node_id_t node_id,
+ int32_t gain) {
+ cb_input_node_gain_changed_called++;
+ cb_context.push_back(context);
+ cb_input_node_gain_changed_node_id.push_back(node_id);
+ cb_input_node_gain_changed_gain.push_back(gain);
+}
+
+/* Number of active streams changed. */
+void cb_num_active_streams_changed(void *context,
+ enum CRAS_STREAM_DIRECTION dir,
+ uint32_t num_active_streams) {
+ cb_num_active_streams_changed_called++;
+ cb_context.push_back(context);
+ cb_num_active_streams_changed_dir.push_back(dir);
+ cb_num_active_streams_changed_num.push_back(num_active_streams);
+}
+
+class ObserverTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ int rc;
+
+ ResetStubData();
+ rc = cras_observer_server_init();
+ ASSERT_EQ(0, rc);
+ EXPECT_EQ(13, cras_alert_create_called);
+ EXPECT_EQ(reinterpret_cast<void *>(output_volume_alert),
+ cras_alert_add_callback_map[g_observer->alerts.output_volume]);
+ EXPECT_EQ(reinterpret_cast<void *>(output_mute_alert),
+ cras_alert_add_callback_map[g_observer->alerts.output_mute]);
+ EXPECT_EQ(reinterpret_cast<void *>(capture_gain_alert),
+ cras_alert_add_callback_map[g_observer->alerts.capture_gain]);
+ EXPECT_EQ(reinterpret_cast<void *>(capture_mute_alert),
+ cras_alert_add_callback_map[g_observer->alerts.capture_mute]);
+ EXPECT_EQ(reinterpret_cast<void *>(nodes_alert),
+ cras_alert_add_callback_map[g_observer->alerts.nodes]);
+ EXPECT_EQ(reinterpret_cast<void *>(nodes_prepare),
+ cras_alert_create_prepare_map[g_observer->alerts.nodes]);
+ EXPECT_EQ(reinterpret_cast<void *>(active_node_alert),
+ cras_alert_add_callback_map[g_observer->alerts.active_node]);
+ EXPECT_EQ(CRAS_ALERT_FLAG_KEEP_ALL_DATA,
+ cras_alert_create_flags_map[g_observer->alerts.active_node]);
+ EXPECT_EQ(reinterpret_cast<void *>(output_node_volume_alert),
+ cras_alert_add_callback_map[g_observer->alerts.output_node_volume]);
+ EXPECT_EQ(reinterpret_cast<void *>(node_left_right_swapped_alert),
+ cras_alert_add_callback_map[g_observer->alerts.node_left_right_swapped]);
+ EXPECT_EQ(reinterpret_cast<void *>(input_node_gain_alert),
+ cras_alert_add_callback_map[g_observer->alerts.input_node_gain]);
+ EXPECT_EQ(reinterpret_cast<void *>(num_active_streams_alert),
+ cras_alert_add_callback_map[g_observer->alerts.num_active_streams[
+ CRAS_STREAM_OUTPUT]]);
+ EXPECT_EQ(reinterpret_cast<void *>(num_active_streams_alert),
+ cras_alert_add_callback_map[g_observer->alerts.num_active_streams[
+ CRAS_STREAM_INPUT]]);
+ EXPECT_EQ(reinterpret_cast<void *>(num_active_streams_alert),
+ cras_alert_add_callback_map[g_observer->alerts.num_active_streams[
+ CRAS_STREAM_POST_MIX_PRE_DSP]]);
+ EXPECT_EQ(reinterpret_cast<void *>(suspend_changed_alert),
+ cras_alert_add_callback_map[g_observer->alerts.suspend_changed]);
+
+ cras_observer_get_ops(NULL, &ops1_);
+ EXPECT_NE(0, cras_observer_ops_are_empty(&ops1_));
+
+ cras_observer_get_ops(NULL, &ops2_);
+ EXPECT_NE(0, cras_observer_ops_are_empty(&ops2_));
+
+ context1_ = reinterpret_cast<void *>(1);
+ context2_ = reinterpret_cast<void *>(2);
+ }
+
+ virtual void TearDown() {
+ cras_observer_server_free();
+ EXPECT_EQ(13, cras_alert_destroy_called);
+ ResetStubData();
+ }
+
+ void DoObserverAlert(cras_alert_cb alert, void *data) {
+ client1_ = cras_observer_add(&ops1_, context1_);
+ client2_ = cras_observer_add(&ops2_, context2_);
+ ASSERT_NE(client1_, reinterpret_cast<struct cras_observer_client *>(NULL));
+ ASSERT_NE(client2_, reinterpret_cast<struct cras_observer_client *>(NULL));
+
+ ASSERT_NE(alert, reinterpret_cast<cras_alert_cb>(NULL));
+ alert(NULL, data);
+
+ EXPECT_EQ(cb_context[0], context1_);
+ EXPECT_EQ(cb_context[1], context2_);
+ }
+
+ void DoObserverRemoveClear(cras_alert_cb alert, void *data) {
+ ASSERT_NE(alert, reinterpret_cast<cras_alert_cb>(NULL));
+ ASSERT_NE(client1_, reinterpret_cast<struct cras_observer_client *>(NULL));
+ ASSERT_NE(client2_, reinterpret_cast<struct cras_observer_client *>(NULL));
+
+ // Test observer removal.
+ cras_observer_remove(client1_);
+ cb_context.clear();
+ alert(NULL, data);
+ EXPECT_EQ(cb_context[0], context2_);
+ EXPECT_EQ(cb_context.size(), 1);
+
+ // Clear out ops1_.
+ cras_observer_get_ops(NULL, &ops1_);
+ EXPECT_NE(0, cras_observer_ops_are_empty(&ops1_));
+
+ // Get the current value of ops2_ into ops1_.
+ cras_observer_get_ops(client2_, &ops1_);
+ EXPECT_EQ(0, memcmp((void *)&ops1_, (void *)&ops2_, sizeof(ops1_)));
+
+ // Clear out opts for client2.
+ cras_observer_get_ops(NULL, &ops2_);
+ EXPECT_NE(0, cras_observer_ops_are_empty(&ops2_));
+ cras_observer_set_ops(client2_, &ops2_);
+
+ cb_context.clear();
+ alert(NULL, data);
+ // No callbacks executed.
+ EXPECT_EQ(cb_context.size(), 0);
+ }
+
+ struct cras_observer_client *client1_;
+ struct cras_observer_client *client2_;
+ struct cras_observer_ops ops1_;
+ struct cras_observer_ops ops2_;
+ void *context1_;
+ void *context2_;
+};
+
+TEST_F(ObserverTest, NotifyOutputVolume) {
+ struct cras_observer_alert_data_volume *data;
+ const int32_t volume = 100;
+
+ cras_observer_notify_output_volume(volume);
+ EXPECT_EQ(cras_alert_pending_alert_value, g_observer->alerts.output_volume);
+ ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
+ ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
+ data = reinterpret_cast<struct cras_observer_alert_data_volume *>(
+ cras_alert_pending_data_value);
+ EXPECT_EQ(data->volume, volume);
+
+ ops1_.output_volume_changed = cb_output_volume_changed;
+ ops2_.output_volume_changed = cb_output_volume_changed;
+ DoObserverAlert(output_volume_alert, data);
+ ASSERT_EQ(2, cb_output_volume_changed_called);
+ EXPECT_EQ(cb_output_volume_changed_volume[0], volume);
+ EXPECT_EQ(cb_output_volume_changed_volume[1], volume);
+
+ DoObserverRemoveClear(output_volume_alert, data);
+};
+
+TEST_F(ObserverTest, NotifyOutputMute) {
+ struct cras_observer_alert_data_mute *data;
+ const int muted = 1;
+ const int user_muted = 0;
+ const int mute_locked = 0;
+
+ cras_observer_notify_output_mute(muted, user_muted, mute_locked);
+ EXPECT_EQ(cras_alert_pending_alert_value, g_observer->alerts.output_mute);
+ ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
+ ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
+ data = reinterpret_cast<struct cras_observer_alert_data_mute *>(
+ cras_alert_pending_data_value);
+ EXPECT_EQ(data->muted, muted);
+ EXPECT_EQ(data->user_muted, user_muted);
+ EXPECT_EQ(data->mute_locked, mute_locked);
+
+ ops1_.output_mute_changed = cb_output_mute_changed;
+ ops2_.output_mute_changed = cb_output_mute_changed;
+ DoObserverAlert(output_mute_alert, data);
+ ASSERT_EQ(2, cb_output_mute_changed_called);
+ EXPECT_EQ(cb_output_mute_changed_muted[0], muted);
+ EXPECT_EQ(cb_output_mute_changed_muted[1], muted);
+ EXPECT_EQ(cb_output_mute_changed_user_muted[0], user_muted);
+ EXPECT_EQ(cb_output_mute_changed_user_muted[1], user_muted);
+ EXPECT_EQ(cb_output_mute_changed_mute_locked[0], mute_locked);
+ EXPECT_EQ(cb_output_mute_changed_mute_locked[1], mute_locked);
+
+ DoObserverRemoveClear(output_mute_alert, data);
+};
+
+TEST_F(ObserverTest, NotifyCaptureGain) {
+ struct cras_observer_alert_data_volume *data;
+ const int32_t gain = -20;
+
+ cras_observer_notify_capture_gain(gain);
+ EXPECT_EQ(cras_alert_pending_alert_value, g_observer->alerts.capture_gain);
+ ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
+ ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
+ data = reinterpret_cast<struct cras_observer_alert_data_volume *>(
+ cras_alert_pending_data_value);
+ EXPECT_EQ(data->volume, gain);
+
+ ops1_.capture_gain_changed = cb_capture_gain_changed;
+ ops2_.capture_gain_changed = cb_capture_gain_changed;
+ DoObserverAlert(capture_gain_alert, data);
+ ASSERT_EQ(2, cb_capture_gain_changed_called);
+ EXPECT_EQ(cb_capture_gain_changed_gain[0], gain);
+ EXPECT_EQ(cb_capture_gain_changed_gain[1], gain);
+
+ DoObserverRemoveClear(capture_gain_alert, data);
+};
+
+TEST_F(ObserverTest, NotifyCaptureMute) {
+ struct cras_observer_alert_data_mute *data;
+ const int muted = 1;
+ const int mute_locked = 0;
+
+ cras_observer_notify_capture_mute(muted, mute_locked);
+ EXPECT_EQ(cras_alert_pending_alert_value, g_observer->alerts.capture_mute);
+ ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
+ ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
+ data = reinterpret_cast<struct cras_observer_alert_data_mute *>(
+ cras_alert_pending_data_value);
+ EXPECT_EQ(data->muted, muted);
+ EXPECT_EQ(data->mute_locked, mute_locked);
+
+ ops1_.capture_mute_changed = cb_capture_mute_changed;
+ ops2_.capture_mute_changed = cb_capture_mute_changed;
+ DoObserverAlert(capture_mute_alert, data);
+ ASSERT_EQ(2, cb_capture_mute_changed_called);
+ EXPECT_EQ(cb_capture_mute_changed_muted[0], muted);
+ EXPECT_EQ(cb_capture_mute_changed_muted[1], muted);
+ EXPECT_EQ(cb_capture_mute_changed_mute_locked[0], mute_locked);
+ EXPECT_EQ(cb_capture_mute_changed_mute_locked[1], mute_locked);
+
+ DoObserverRemoveClear(capture_mute_alert, data);
+};
+
+TEST_F(ObserverTest, NotifyNodes) {
+ cras_observer_notify_nodes();
+ EXPECT_EQ(cras_alert_pending_alert_value, g_observer->alerts.nodes);
+
+ ops1_.nodes_changed = cb_nodes_changed;
+ ops2_.nodes_changed = cb_nodes_changed;
+ DoObserverAlert(nodes_alert, NULL);
+ ASSERT_EQ(2, cb_nodes_changed_called);
+
+ DoObserverRemoveClear(nodes_alert, NULL);
+};
+
+TEST_F(ObserverTest, NotifyActiveNode) {
+ struct cras_observer_alert_data_active_node *data;
+ const enum CRAS_STREAM_DIRECTION dir = CRAS_STREAM_INPUT;
+ const cras_node_id_t node_id = 0x0001000100020002;
+
+ cras_observer_notify_active_node(dir, node_id);
+ EXPECT_EQ(cras_alert_pending_alert_value, g_observer->alerts.active_node);
+ ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
+ ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
+ data = reinterpret_cast<struct cras_observer_alert_data_active_node *>(
+ cras_alert_pending_data_value);
+ EXPECT_EQ(data->node_id, node_id);
+ EXPECT_EQ(data->direction, dir);
+
+ ops1_.active_node_changed = cb_active_node_changed;
+ ops2_.active_node_changed = cb_active_node_changed;
+ DoObserverAlert(active_node_alert, data);
+ ASSERT_EQ(2, cb_active_node_changed_called);
+ EXPECT_EQ(cb_active_node_changed_dir[0], dir);
+ EXPECT_EQ(cb_active_node_changed_dir[1], dir);
+ EXPECT_EQ(cb_active_node_changed_node_id[0], node_id);
+ EXPECT_EQ(cb_active_node_changed_node_id[1], node_id);
+
+ DoObserverRemoveClear(active_node_alert, data);
+};
+
+TEST_F(ObserverTest, NotifyOutputNodeVolume) {
+ struct cras_observer_alert_data_node_volume *data;
+ const cras_node_id_t node_id = 0x0001000100020002;
+ const int32_t volume = 100;
+
+ cras_observer_notify_output_node_volume(node_id, volume);
+ EXPECT_EQ(cras_alert_pending_alert_value,
+ g_observer->alerts.output_node_volume);
+ ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
+ ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
+ data = reinterpret_cast<struct cras_observer_alert_data_node_volume *>(
+ cras_alert_pending_data_value);
+ EXPECT_EQ(data->node_id, node_id);
+ EXPECT_EQ(data->volume, volume);
+
+ ops1_.output_node_volume_changed = cb_output_node_volume_changed;
+ ops2_.output_node_volume_changed = cb_output_node_volume_changed;
+ DoObserverAlert(output_node_volume_alert, data);
+ ASSERT_EQ(2, cb_output_node_volume_changed_called);
+ EXPECT_EQ(cb_output_node_volume_changed_volume[0], volume);
+ EXPECT_EQ(cb_output_node_volume_changed_volume[1], volume);
+ EXPECT_EQ(cb_output_node_volume_changed_node_id[0], node_id);
+ EXPECT_EQ(cb_output_node_volume_changed_node_id[1], node_id);
+
+ DoObserverRemoveClear(output_node_volume_alert, data);
+};
+
+TEST_F(ObserverTest, NotifyNodeLeftRightSwapped) {
+ struct cras_observer_alert_data_node_lr_swapped *data;
+ const cras_node_id_t node_id = 0x0001000100020002;
+ const int swapped = 1;
+
+ cras_observer_notify_node_left_right_swapped(node_id, swapped);
+ EXPECT_EQ(cras_alert_pending_alert_value,
+ g_observer->alerts.node_left_right_swapped);
+ ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
+ ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
+ data = reinterpret_cast<struct cras_observer_alert_data_node_lr_swapped *>(
+ cras_alert_pending_data_value);
+ EXPECT_EQ(data->node_id, node_id);
+ EXPECT_EQ(data->swapped, swapped);
+
+ ops1_.node_left_right_swapped_changed = cb_node_left_right_swapped_changed;
+ ops2_.node_left_right_swapped_changed = cb_node_left_right_swapped_changed;
+ DoObserverAlert(node_left_right_swapped_alert, data);
+ ASSERT_EQ(2, cb_node_left_right_swapped_changed_called);
+ EXPECT_EQ(cb_node_left_right_swapped_changed_swapped[0], swapped);
+ EXPECT_EQ(cb_node_left_right_swapped_changed_swapped[1], swapped);
+ EXPECT_EQ(cb_node_left_right_swapped_changed_node_id[0], node_id);
+ EXPECT_EQ(cb_node_left_right_swapped_changed_node_id[1], node_id);
+
+ DoObserverRemoveClear(node_left_right_swapped_alert, data);
+};
+
+TEST_F(ObserverTest, NotifyInputNodeGain) {
+ struct cras_observer_alert_data_node_volume *data;
+ const cras_node_id_t node_id = 0x0001000100020002;
+ const int32_t gain = -20;
+
+ cras_observer_notify_input_node_gain(node_id, gain);
+ EXPECT_EQ(cras_alert_pending_alert_value,
+ g_observer->alerts.input_node_gain);
+ ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
+ ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
+ data = reinterpret_cast<struct cras_observer_alert_data_node_volume *>(
+ cras_alert_pending_data_value);
+ EXPECT_EQ(data->node_id, node_id);
+ EXPECT_EQ(data->volume, gain);
+
+ ops1_.input_node_gain_changed = cb_input_node_gain_changed;
+ ops2_.input_node_gain_changed = cb_input_node_gain_changed;
+ DoObserverAlert(input_node_gain_alert, data);
+ ASSERT_EQ(2, cb_input_node_gain_changed_called);
+ EXPECT_EQ(cb_input_node_gain_changed_gain[0], gain);
+ EXPECT_EQ(cb_input_node_gain_changed_gain[1], gain);
+ EXPECT_EQ(cb_input_node_gain_changed_node_id[0], node_id);
+ EXPECT_EQ(cb_input_node_gain_changed_node_id[1], node_id);
+
+ DoObserverRemoveClear(input_node_gain_alert, data);
+};
+
+TEST_F(ObserverTest, NotifySuspendChanged) {
+ struct cras_observer_alert_data_suspend *data;
+
+ cras_observer_notify_suspend_changed(1);
+ EXPECT_EQ(cras_alert_pending_alert_value,
+ g_observer->alerts.suspend_changed);
+ ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
+ ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
+ data = reinterpret_cast<struct cras_observer_alert_data_suspend *>(
+ cras_alert_pending_data_value);
+ EXPECT_EQ(data->suspended, 1);
+
+ cras_observer_notify_suspend_changed(0);
+ EXPECT_EQ(cras_alert_pending_alert_value,
+ g_observer->alerts.suspend_changed);
+ ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
+ ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
+ data = reinterpret_cast<struct cras_observer_alert_data_suspend *>(
+ cras_alert_pending_data_value);
+ EXPECT_EQ(data->suspended, 0);
+}
+
+TEST_F(ObserverTest, NotifyNumActiveStreams) {
+ struct cras_observer_alert_data_streams *data;
+ const enum CRAS_STREAM_DIRECTION dir = CRAS_STREAM_INPUT;
+ const uint32_t active_streams = 10;
+
+ cras_observer_notify_num_active_streams(dir, active_streams);
+ EXPECT_EQ(cras_alert_pending_alert_value,
+ g_observer->alerts.num_active_streams[CRAS_STREAM_INPUT]);
+ ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
+ ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
+ data = reinterpret_cast<struct cras_observer_alert_data_streams *>(
+ cras_alert_pending_data_value);
+ EXPECT_EQ(data->num_active_streams, active_streams);
+ EXPECT_EQ(data->direction, dir);
+
+ ops1_.num_active_streams_changed = cb_num_active_streams_changed;
+ ops2_.num_active_streams_changed = cb_num_active_streams_changed;
+ DoObserverAlert(num_active_streams_alert, data);
+ ASSERT_EQ(2, cb_num_active_streams_changed_called);
+ EXPECT_EQ(cb_num_active_streams_changed_dir[0], dir);
+ EXPECT_EQ(cb_num_active_streams_changed_dir[1], dir);
+ EXPECT_EQ(cb_num_active_streams_changed_num[0], active_streams);
+ EXPECT_EQ(cb_num_active_streams_changed_num[1], active_streams);
+
+ DoObserverRemoveClear(num_active_streams_alert, data);
+};
+
+// Stubs
+extern "C" {
+
+void cras_alert_destroy(struct cras_alert *alert) {
+ cras_alert_destroy_called++;
+}
+
+struct cras_alert *cras_alert_create(cras_alert_prepare prepare,
+ unsigned int flags) {
+ struct cras_alert *alert = NULL;
+
+ cras_alert_create_called++;
+ alert = reinterpret_cast<struct cras_alert*>(cras_alert_create_called);
+ cras_alert_create_return_values.push_back(alert);
+ cras_alert_create_flags_map[alert] = flags;
+ cras_alert_create_prepare_map[alert] = reinterpret_cast<void *>(prepare);
+ return alert;
+}
+
+int cras_alert_add_callback(struct cras_alert *alert, cras_alert_cb cb,
+ void *arg) {
+ cras_alert_add_callback_map[alert] = reinterpret_cast<void *>(cb);
+ return 0;
+}
+
+void cras_alert_pending(struct cras_alert *alert) {
+ cras_alert_pending_alert_value = alert;
+}
+
+void cras_alert_pending_data(struct cras_alert *alert,
+ void *data, size_t data_size) {
+ cras_alert_pending_alert_value = alert;
+ cras_alert_pending_data_size_value = data_size;
+ if (cras_alert_pending_data_value)
+ free(cras_alert_pending_data_value);
+ if (data) {
+ cras_alert_pending_data_value = malloc(data_size);
+ memcpy(cras_alert_pending_data_value, data, data_size);
+ }
+ else
+ cras_alert_pending_data_value = NULL;
+}
+
+void cras_iodev_list_update_device_list() {
+ cras_iodev_list_update_device_list_called++;
+}
+
+} // extern "C"
+
+} // namespace
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ openlog(NULL, LOG_PERROR, LOG_USER);
+ return RUN_ALL_TESTS();
+}
diff --git a/cras/src/tests/ramp_unittest.cc b/cras/src/tests/ramp_unittest.cc
new file mode 100644
index 00000000..7bd8fac1
--- /dev/null
+++ b/cras/src/tests/ramp_unittest.cc
@@ -0,0 +1,406 @@
+// Copyright 2016 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "cras_ramp.c"
+}
+
+static int callback_called;
+static void *callback_arg;
+
+void ResetStubData() {
+ callback_called = 0;
+ callback_arg = NULL;
+}
+
+namespace {
+
+TEST(RampTestSuite, Init) {
+ struct cras_ramp *ramp;
+ struct cras_ramp_action action;
+
+ ResetStubData();
+
+ ramp = cras_ramp_create();
+ action = cras_ramp_get_current_action(ramp);
+
+ EXPECT_EQ(action.type, CRAS_RAMP_ACTION_NONE);
+ EXPECT_FLOAT_EQ(1.0, action.scaler);
+ EXPECT_FLOAT_EQ(0.0, action.increment);
+
+ cras_ramp_destroy(ramp);
+}
+
+TEST(RampTestSuite, RampUpInitialIncrement) {
+ int ramp_up = 1;
+ int duration_frames = 48000;
+ float increment = 1.0 / 48000;
+ cras_ramp *ramp;
+ cras_ramp_action action;
+
+ ResetStubData();
+
+ ramp = cras_ramp_create();
+ cras_ramp_start(ramp, ramp_up, duration_frames, NULL, NULL);
+
+ action = cras_ramp_get_current_action(ramp);
+
+ EXPECT_EQ(CRAS_RAMP_ACTION_PARTIAL, action.type);
+ EXPECT_FLOAT_EQ(0.0, action.scaler);
+ EXPECT_FLOAT_EQ(increment, action.increment);
+
+ cras_ramp_destroy(ramp);
+}
+
+TEST(RampTestSuite, RampUpUpdateRampedFrames) {
+ int ramp_up = 1;
+ int duration_frames = 48000;
+ float increment = 1.0 / 48000;
+ int rc;
+ int ramped_frames = 512;
+ struct cras_ramp *ramp;
+ struct cras_ramp_action action;
+ float scaler = increment * ramped_frames;
+
+ ResetStubData();
+
+ ramp = cras_ramp_create();
+ cras_ramp_start(ramp, ramp_up, duration_frames, NULL, NULL);
+
+ rc = cras_ramp_update_ramped_frames(ramp, ramped_frames);
+
+ action = cras_ramp_get_current_action(ramp);
+
+ EXPECT_EQ(rc, 0);
+ EXPECT_EQ(CRAS_RAMP_ACTION_PARTIAL, action.type);
+ EXPECT_FLOAT_EQ(scaler, action.scaler);
+ EXPECT_FLOAT_EQ(increment, action.increment);
+
+ cras_ramp_destroy(ramp);
+}
+
+TEST(RampTestSuite, RampUpPassedRamp) {
+ int ramp_up = 1;
+ int duration_frames = 48000;
+ int rc;
+ int ramped_frames = 48000;
+ struct cras_ramp *ramp;
+ struct cras_ramp_action action;
+
+ ResetStubData();
+
+ ramp = cras_ramp_create();
+ cras_ramp_start(ramp, ramp_up, duration_frames, NULL, NULL);
+
+ rc = cras_ramp_update_ramped_frames(ramp, ramped_frames);
+
+ action = cras_ramp_get_current_action(ramp);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(CRAS_RAMP_ACTION_NONE, action.type);
+ EXPECT_FLOAT_EQ(1.0, action.scaler);
+ EXPECT_FLOAT_EQ(0.0, action.increment);
+
+ cras_ramp_destroy(ramp);
+}
+
+TEST(RampTestSuite, RampUpWhileHalfWayRampDown) {
+ int ramp_up;
+ int duration_frames = 48000;
+ int rc;
+ int ramped_frames = 24000;
+ struct cras_ramp *ramp;
+ struct cras_ramp_action action;
+ float down_increment = -1.0 / 48000;
+ float up_increment;
+ float scaler;
+
+ ResetStubData();
+
+ ramp = cras_ramp_create();
+ ramp_up = 0;
+ cras_ramp_start(ramp, ramp_up, duration_frames, NULL, NULL);
+
+ rc = cras_ramp_update_ramped_frames(ramp, ramped_frames);
+
+ // Get expected current scaler.
+ scaler = 1 + down_increment * ramped_frames;
+ // The increment will be calculated by ramping to 1 starting from scaler.
+ up_increment = (1 - scaler) / 48000;
+
+ ramp_up = 1;
+ cras_ramp_start(ramp, ramp_up, duration_frames, NULL, NULL);
+
+ action = cras_ramp_get_current_action(ramp);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(CRAS_RAMP_ACTION_PARTIAL, action.type);
+ EXPECT_FLOAT_EQ(scaler, action.scaler);
+ EXPECT_FLOAT_EQ(up_increment, action.increment);
+
+ cras_ramp_destroy(ramp);
+}
+
+TEST(RampTestSuite, RampUpWhileHalfWayRampUp) {
+ int ramp_up;
+ int duration_frames = 48000;
+ int rc;
+ int ramped_frames = 24000;
+ struct cras_ramp *ramp;
+ struct cras_ramp_action action;
+ float first_increment = 1.0 / 48000;
+ float second_increment;
+ float scaler;
+
+ ResetStubData();
+
+ ramp = cras_ramp_create();
+ ramp_up = 1;
+ cras_ramp_start(ramp, ramp_up, duration_frames, NULL, NULL);
+
+ rc = cras_ramp_update_ramped_frames(ramp, ramped_frames);
+
+ // Get expected current scaler.
+ scaler = first_increment * ramped_frames;
+ // The increment will be calculated by ramping to 1 starting from scaler.
+ second_increment = (1 - scaler) / 48000;
+
+ ramp_up = 1;
+ cras_ramp_start(ramp, ramp_up, duration_frames, NULL, NULL);
+
+ action = cras_ramp_get_current_action(ramp);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(CRAS_RAMP_ACTION_PARTIAL, action.type);
+ EXPECT_FLOAT_EQ(scaler, action.scaler);
+ EXPECT_FLOAT_EQ(second_increment, action.increment);
+
+ cras_ramp_destroy(ramp);
+}
+
+TEST(RampTestSuite, RampDownInitialIncrement) {
+ int ramp_up = 0;
+ int duration_frames = 48000;
+ float increment = -1.0 / 48000;
+ cras_ramp *ramp;
+ cras_ramp_action action;
+
+ ResetStubData();
+
+ ramp = cras_ramp_create();
+ cras_ramp_start(ramp, ramp_up, duration_frames, NULL, NULL);
+
+ action = cras_ramp_get_current_action(ramp);
+
+ EXPECT_EQ(CRAS_RAMP_ACTION_PARTIAL, action.type);
+ EXPECT_FLOAT_EQ(1.0, action.scaler);
+ EXPECT_FLOAT_EQ(increment, action.increment);
+
+ cras_ramp_destroy(ramp);
+}
+
+TEST(RampTestSuite, RampDownUpdateRampedFrames) {
+ int ramp_up = 0;
+ int duration_frames = 48000;
+ float increment = -1.0 / 48000;
+ int rc;
+ int ramped_frames = 512;
+ struct cras_ramp *ramp;
+ struct cras_ramp_action action;
+ float scaler = 1 + increment * ramped_frames;
+
+ ResetStubData();
+
+ ramp = cras_ramp_create();
+ cras_ramp_start(ramp, ramp_up, duration_frames, NULL, NULL);
+
+ rc = cras_ramp_update_ramped_frames(ramp, ramped_frames);
+
+ action = cras_ramp_get_current_action(ramp);
+
+ EXPECT_EQ(rc, 0);
+ EXPECT_EQ(CRAS_RAMP_ACTION_PARTIAL, action.type);
+ EXPECT_FLOAT_EQ(scaler, action.scaler);
+ EXPECT_FLOAT_EQ(increment, action.increment);
+
+ cras_ramp_destroy(ramp);
+}
+
+TEST(RampTestSuite, RampDownPassedRamp) {
+ int ramp_up = 0;
+ int duration_frames = 48000;
+ int rc;
+ int ramped_frames = 48000;
+ struct cras_ramp *ramp;
+ struct cras_ramp_action action;
+
+ ResetStubData();
+
+ ramp = cras_ramp_create();
+ cras_ramp_start(ramp, ramp_up, duration_frames, NULL, NULL);
+
+ rc = cras_ramp_update_ramped_frames(ramp, ramped_frames);
+
+ action = cras_ramp_get_current_action(ramp);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(CRAS_RAMP_ACTION_NONE, action.type);
+ EXPECT_FLOAT_EQ(1.0, action.scaler);
+ EXPECT_FLOAT_EQ(0.0, action.increment);
+
+ cras_ramp_destroy(ramp);
+}
+
+TEST(RampTestSuite, RampDownWhileHalfWayRampUp) {
+ int ramp_up;
+ int duration_frames = 48000;
+ int rc;
+ int ramped_frames = 24000;
+ struct cras_ramp *ramp;
+ struct cras_ramp_action action;
+ float up_increment = 1.0 / 48000;
+ float down_increment;
+ float scaler;
+
+ ResetStubData();
+
+ ramp = cras_ramp_create();
+ // Ramp up first.
+ ramp_up = 1;
+ cras_ramp_start(ramp, ramp_up, duration_frames, NULL, NULL);
+
+ rc = cras_ramp_update_ramped_frames(ramp, ramped_frames);
+
+ // Get expected current scaler.
+ scaler = up_increment * ramped_frames;
+ // The increment will be calculated by ramping to 0 starting from scaler.
+ down_increment = -scaler / duration_frames;
+
+
+ // Ramp down will start from current scaler.
+ ramp_up = 0;
+ cras_ramp_start(ramp, ramp_up, duration_frames, NULL, NULL);
+
+ action = cras_ramp_get_current_action(ramp);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(CRAS_RAMP_ACTION_PARTIAL, action.type);
+ EXPECT_FLOAT_EQ(scaler, action.scaler);
+ EXPECT_FLOAT_EQ(down_increment, action.increment);
+
+ cras_ramp_destroy(ramp);
+}
+
+TEST(RampTestSuite, RampDownWhileHalfWayRampDown) {
+ int ramp_up;
+ int duration_frames = 48000;
+ int rc;
+ int ramped_frames = 24000;
+ struct cras_ramp *ramp;
+ struct cras_ramp_action action;
+ float down_increment = -1.0 / 48000;
+ float second_down_increment;
+ float scaler;
+
+ ResetStubData();
+
+ ramp = cras_ramp_create();
+ // Ramp down.
+ ramp_up = 0;
+ cras_ramp_start(ramp, ramp_up, duration_frames, NULL, NULL);
+
+ rc = cras_ramp_update_ramped_frames(ramp, ramped_frames);
+
+ // Get expected current scaler.
+ scaler = 1 + down_increment * ramped_frames;
+ // The increment will be calculated by ramping to 0 starting from scaler.
+ second_down_increment = -scaler / duration_frames;
+
+
+ // Ramp down starting from current scaler.
+ ramp_up = 0;
+ cras_ramp_start(ramp, ramp_up, duration_frames, NULL, NULL);
+
+ action = cras_ramp_get_current_action(ramp);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(CRAS_RAMP_ACTION_PARTIAL, action.type);
+ EXPECT_FLOAT_EQ(scaler, action.scaler);
+ EXPECT_FLOAT_EQ(second_down_increment, action.increment);
+
+ cras_ramp_destroy(ramp);
+}
+
+void ramp_callback(void *arg) {
+ callback_called++;
+ callback_arg = arg;
+}
+
+TEST(RampTestSuite, RampUpPassedRampCallback) {
+ int ramp_up = 1;
+ int duration_frames = 48000;
+ int rc;
+ int ramped_frames = 48000;
+ struct cras_ramp *ramp;
+ struct cras_ramp_action action;
+ void *cb_data = reinterpret_cast<void*>(0x123);
+
+ ResetStubData();
+
+ ramp = cras_ramp_create();
+ cras_ramp_start(ramp, ramp_up, duration_frames, ramp_callback, cb_data);
+
+ rc = cras_ramp_update_ramped_frames(ramp, ramped_frames);
+
+ action = cras_ramp_get_current_action(ramp);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(CRAS_RAMP_ACTION_NONE, action.type);
+ EXPECT_FLOAT_EQ(1.0, action.scaler);
+ EXPECT_FLOAT_EQ(0.0, action.increment);
+ EXPECT_EQ(1, callback_called);
+ EXPECT_EQ(cb_data, callback_arg);
+
+ cras_ramp_destroy(ramp);
+}
+
+TEST(RampTestSuite, RampDownPassedRampCallback) {
+ int ramp_up = 0;
+ int duration_frames = 48000;
+ int rc;
+ int ramped_frames = 48000;
+ struct cras_ramp *ramp;
+ struct cras_ramp_action action;
+ void *cb_data = reinterpret_cast<void*>(0x123);
+
+ ResetStubData();
+
+ ramp = cras_ramp_create();
+ cras_ramp_start(ramp, ramp_up, duration_frames, ramp_callback, cb_data);
+
+ rc = cras_ramp_update_ramped_frames(ramp, ramped_frames);
+
+ action = cras_ramp_get_current_action(ramp);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(CRAS_RAMP_ACTION_NONE, action.type);
+ EXPECT_FLOAT_EQ(1.0, action.scaler);
+ EXPECT_FLOAT_EQ(0.0, action.increment);
+ EXPECT_EQ(1, callback_called);
+ EXPECT_EQ(cb_data, callback_arg);
+
+ cras_ramp_destroy(ramp);
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int rc = RUN_ALL_TESTS();
+
+ return rc;
+}
diff --git a/cras/src/tests/rclient_unittest.cc b/cras/src/tests/rclient_unittest.cc
index c16dc52d..e699c7e4 100644
--- a/cras/src/tests/rclient_unittest.cc
+++ b/cras/src/tests/rclient_unittest.cc
@@ -12,6 +12,9 @@ extern "C" {
#include "cras_rclient.h"
#include "cras_rstream.h"
#include "cras_system_state.h"
+
+// Access to data structures and static functions.
+#include "cras_rclient.c"
}
// Stub data.
@@ -40,6 +43,17 @@ static unsigned int stream_list_disconnect_stream_called;
static unsigned int cras_iodev_list_rm_input_called;
static unsigned int cras_iodev_list_rm_output_called;
static struct cras_rstream dummy_rstream;
+static size_t cras_observer_num_ops_registered;
+static size_t cras_observer_register_notify_called;
+static size_t cras_observer_add_called;
+static void *cras_observer_add_context_value;
+static struct cras_observer_client *cras_observer_add_return_value;
+static size_t cras_observer_get_ops_called;
+static struct cras_observer_ops cras_observer_ops_value;
+static size_t cras_observer_set_ops_called;
+static size_t cras_observer_ops_are_empty_called;
+static struct cras_observer_ops cras_observer_ops_are_empty_empty_ops;
+static size_t cras_observer_remove_called;
void ResetStubData() {
cras_rstream_create_return = 0;
@@ -66,6 +80,19 @@ void ResetStubData() {
stream_list_disconnect_stream_called = 0;
cras_iodev_list_rm_output_called = 0;
cras_iodev_list_rm_input_called = 0;
+ cras_observer_num_ops_registered = 0;
+ cras_observer_register_notify_called = 0;
+ cras_observer_add_called = 0;
+ cras_observer_add_return_value =
+ reinterpret_cast<struct cras_observer_client *>(1);
+ cras_observer_add_context_value = NULL;
+ cras_observer_get_ops_called = 0;
+ memset(&cras_observer_ops_value, 0, sizeof(cras_observer_ops_value));
+ cras_observer_set_ops_called = 0;
+ cras_observer_ops_are_empty_called = 0;
+ memset(&cras_observer_ops_are_empty_empty_ops, 0,
+ sizeof(cras_observer_ops_are_empty_empty_ops));
+ cras_observer_remove_called = 0;
}
namespace {
@@ -87,7 +114,6 @@ TEST(RClientSuite, CreateSendMessage) {
rc = read(pipe_fds[0], &msg, sizeof(msg));
EXPECT_EQ(sizeof(msg), rc);
EXPECT_EQ(CRAS_CLIENT_CONNECTED, msg.header.id);
- EXPECT_EQ(CRAS_CLIENT_CONNECTED, msg.header.id);
cras_rclient_destroy(rclient);
close(pipe_fds[0]);
@@ -134,6 +160,9 @@ class RClientMessagesSuite : public testing::Test {
close(pipe_fds_[1]);
}
+ void RegisterNotification(enum CRAS_CLIENT_MESSAGE_ID msg_id,
+ void *callback, void **ops_address);
+
struct cras_connect_message connect_msg_;
struct cras_rclient *rclient_;
struct cras_rstream *rstream_;
@@ -295,6 +324,301 @@ TEST_F(RClientMessagesSuite, SetCaptureMute) {
EXPECT_EQ(1, cras_system_set_capture_mute_locked_value);
}
+void RClientMessagesSuite::RegisterNotification(
+ enum CRAS_CLIENT_MESSAGE_ID msg_id,
+ void *callback, void **ops_address) {
+ struct cras_register_notification msg;
+ int do_register = callback != NULL ? 1 : 0;
+ int rc;
+
+ cras_observer_register_notify_called++;
+
+ cras_fill_register_notification_message(&msg, msg_id, do_register);
+ EXPECT_EQ(msg.header.length, sizeof(msg));
+ EXPECT_EQ(msg.header.id, CRAS_SERVER_REGISTER_NOTIFICATION);
+ EXPECT_EQ(msg.do_register, do_register);
+ EXPECT_EQ(msg.msg_id, msg_id);
+
+ rc = cras_rclient_message_from_client(rclient_, &msg.header, -1);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(cras_observer_register_notify_called, cras_observer_get_ops_called);
+ EXPECT_EQ(cras_observer_register_notify_called,\
+ cras_observer_ops_are_empty_called);
+ if (msg.do_register)
+ cras_observer_num_ops_registered++;
+ if (cras_observer_num_ops_registered == 1) {
+ if (msg.do_register) {
+ EXPECT_EQ(1, cras_observer_add_called);
+ EXPECT_EQ(rclient_, cras_observer_add_context_value);
+ EXPECT_EQ(rclient_->observer, cras_observer_add_return_value);
+ } else {
+ EXPECT_EQ(1, cras_observer_remove_called);
+ EXPECT_EQ(rclient_->observer, (struct cras_observer_client *)NULL);
+ }
+ } else {
+ EXPECT_EQ(cras_observer_register_notify_called - 1,\
+ cras_observer_set_ops_called);
+ }
+ if (!msg.do_register)
+ cras_observer_num_ops_registered--;
+ if (cras_observer_num_ops_registered)
+ EXPECT_EQ(callback, *ops_address);
+}
+
+TEST_F(RClientMessagesSuite, RegisterStatusNotification) {
+ /* First registration for this client. */
+ RegisterNotification(
+ CRAS_CLIENT_OUTPUT_VOLUME_CHANGED,
+ (void *)send_output_volume_changed,
+ (void **)&cras_observer_ops_value.output_volume_changed);
+
+ /* Second registration for this client. */
+ RegisterNotification(
+ CRAS_CLIENT_CAPTURE_GAIN_CHANGED,
+ (void *)send_capture_gain_changed,
+ (void **)&cras_observer_ops_value.capture_gain_changed);
+
+ /* Deregister output_volume. */
+ RegisterNotification(
+ CRAS_CLIENT_OUTPUT_VOLUME_CHANGED, NULL,
+ (void **)&cras_observer_ops_value.output_volume_changed);
+
+ /* Register/deregister all msg_ids. */
+
+ RegisterNotification(
+ CRAS_CLIENT_OUTPUT_MUTE_CHANGED,
+ (void *)send_output_mute_changed,
+ (void **)&cras_observer_ops_value.output_mute_changed);
+ RegisterNotification(
+ CRAS_CLIENT_OUTPUT_MUTE_CHANGED, NULL,
+ (void **)&cras_observer_ops_value.output_mute_changed);
+
+ RegisterNotification(
+ CRAS_CLIENT_CAPTURE_MUTE_CHANGED,
+ (void *)send_capture_mute_changed,
+ (void **)&cras_observer_ops_value.capture_mute_changed);
+ RegisterNotification(
+ CRAS_CLIENT_CAPTURE_MUTE_CHANGED, NULL,
+ (void **)&cras_observer_ops_value.capture_mute_changed);
+
+ RegisterNotification(
+ CRAS_CLIENT_NODES_CHANGED,
+ (void *)send_nodes_changed,
+ (void **)&cras_observer_ops_value.nodes_changed);
+ RegisterNotification(
+ CRAS_CLIENT_NODES_CHANGED, NULL,
+ (void **)&cras_observer_ops_value.nodes_changed);
+
+ RegisterNotification(
+ CRAS_CLIENT_ACTIVE_NODE_CHANGED,
+ (void *)send_active_node_changed,
+ (void **)&cras_observer_ops_value.active_node_changed);
+ RegisterNotification(
+ CRAS_CLIENT_ACTIVE_NODE_CHANGED, NULL,
+ (void **)&cras_observer_ops_value.active_node_changed);
+
+ RegisterNotification(
+ CRAS_CLIENT_OUTPUT_NODE_VOLUME_CHANGED,
+ (void *)send_output_node_volume_changed,
+ (void **)&cras_observer_ops_value.output_node_volume_changed);
+ RegisterNotification(
+ CRAS_CLIENT_OUTPUT_NODE_VOLUME_CHANGED, NULL,
+ (void **)&cras_observer_ops_value.output_node_volume_changed);
+
+ RegisterNotification(
+ CRAS_CLIENT_NODE_LEFT_RIGHT_SWAPPED_CHANGED,
+ (void *)send_node_left_right_swapped_changed,
+ (void **)&cras_observer_ops_value.node_left_right_swapped_changed);
+ RegisterNotification(
+ CRAS_CLIENT_NODE_LEFT_RIGHT_SWAPPED_CHANGED, NULL,
+ (void **)&cras_observer_ops_value.node_left_right_swapped_changed);
+
+ RegisterNotification(
+ CRAS_CLIENT_INPUT_NODE_GAIN_CHANGED,
+ (void *)send_input_node_gain_changed,
+ (void **)&cras_observer_ops_value.input_node_gain_changed);
+ RegisterNotification(
+ CRAS_CLIENT_INPUT_NODE_GAIN_CHANGED, NULL,
+ (void **)&cras_observer_ops_value.input_node_gain_changed);
+
+ RegisterNotification(
+ CRAS_CLIENT_NUM_ACTIVE_STREAMS_CHANGED,
+ (void *)send_num_active_streams_changed,
+ (void **)&cras_observer_ops_value.num_active_streams_changed);
+ RegisterNotification(
+ CRAS_CLIENT_NUM_ACTIVE_STREAMS_CHANGED, NULL,
+ (void **)&cras_observer_ops_value.num_active_streams_changed);
+
+ /* Deregister last. */
+ RegisterNotification(
+ CRAS_CLIENT_CAPTURE_GAIN_CHANGED, NULL,
+ (void **)&cras_observer_ops_value.capture_gain_changed);
+}
+
+TEST_F(RClientMessagesSuite, SendOutputVolumeChanged) {
+ void *void_client = reinterpret_cast<void *>(rclient_);
+ char buf[1024];
+ ssize_t rc;
+ struct cras_client_volume_changed *msg =
+ (struct cras_client_volume_changed *)buf;
+ const int32_t volume = 90;
+
+ send_output_volume_changed(void_client, volume);
+ rc = read(pipe_fds_[0], buf, sizeof(buf));
+ ASSERT_EQ(rc, (ssize_t)sizeof(*msg));
+ EXPECT_EQ(msg->header.id, CRAS_CLIENT_OUTPUT_VOLUME_CHANGED);
+ EXPECT_EQ(msg->volume, volume);
+}
+
+TEST_F(RClientMessagesSuite, SendOutputMuteChanged) {
+ void *void_client = reinterpret_cast<void *>(rclient_);
+ char buf[1024];
+ ssize_t rc;
+ struct cras_client_mute_changed *msg =
+ (struct cras_client_mute_changed *)buf;
+ const int muted = 1;
+ const int user_muted = 0;
+ const int mute_locked = 1;
+
+ send_output_mute_changed(void_client, muted, user_muted, mute_locked);
+ rc = read(pipe_fds_[0], buf, sizeof(buf));
+ ASSERT_EQ(rc, (ssize_t)sizeof(*msg));
+ EXPECT_EQ(msg->header.id, CRAS_CLIENT_OUTPUT_MUTE_CHANGED);
+ EXPECT_EQ(msg->muted, muted);
+ EXPECT_EQ(msg->user_muted, user_muted);
+ EXPECT_EQ(msg->mute_locked, mute_locked);
+}
+
+TEST_F(RClientMessagesSuite, SendCaptureGainChanged) {
+ void *void_client = reinterpret_cast<void *>(rclient_);
+ char buf[1024];
+ ssize_t rc;
+ struct cras_client_volume_changed *msg =
+ (struct cras_client_volume_changed *)buf;
+ const int32_t gain = 90;
+
+ send_capture_gain_changed(void_client, gain);
+ rc = read(pipe_fds_[0], buf, sizeof(buf));
+ ASSERT_EQ(rc, (ssize_t)sizeof(*msg));
+ EXPECT_EQ(msg->header.id, CRAS_CLIENT_CAPTURE_GAIN_CHANGED);
+ EXPECT_EQ(msg->volume, gain);
+}
+
+TEST_F(RClientMessagesSuite, SendCaptureMuteChanged) {
+ void *void_client = reinterpret_cast<void *>(rclient_);
+ char buf[1024];
+ ssize_t rc;
+ struct cras_client_mute_changed *msg =
+ (struct cras_client_mute_changed *)buf;
+ const int muted = 1;
+ const int mute_locked = 0;
+
+ send_capture_mute_changed(void_client, muted, mute_locked);
+ rc = read(pipe_fds_[0], buf, sizeof(buf));
+ ASSERT_EQ(rc, (ssize_t)sizeof(*msg));
+ EXPECT_EQ(msg->header.id, CRAS_CLIENT_CAPTURE_MUTE_CHANGED);
+ EXPECT_EQ(msg->muted, muted);
+ EXPECT_EQ(msg->mute_locked, mute_locked);
+}
+
+TEST_F(RClientMessagesSuite, SendNodesChanged) {
+ void *void_client = reinterpret_cast<void *>(rclient_);
+ char buf[1024];
+ ssize_t rc;
+ struct cras_client_nodes_changed *msg =
+ (struct cras_client_nodes_changed *)buf;
+
+ send_nodes_changed(void_client);
+ rc = read(pipe_fds_[0], buf, sizeof(buf));
+ ASSERT_EQ(rc, (ssize_t)sizeof(*msg));
+ EXPECT_EQ(msg->header.id, CRAS_CLIENT_NODES_CHANGED);
+}
+
+TEST_F(RClientMessagesSuite, SendActiveNodeChanged) {
+ void *void_client = reinterpret_cast<void *>(rclient_);
+ char buf[1024];
+ ssize_t rc;
+ struct cras_client_active_node_changed *msg =
+ (struct cras_client_active_node_changed *)buf;
+ const enum CRAS_STREAM_DIRECTION dir = CRAS_STREAM_INPUT;
+ const cras_node_id_t node_id = 0x0001000200030004;
+
+ send_active_node_changed(void_client, dir, node_id);
+ rc = read(pipe_fds_[0], buf, sizeof(buf));
+ ASSERT_EQ(rc, (ssize_t)sizeof(*msg));
+ EXPECT_EQ(msg->header.id, CRAS_CLIENT_ACTIVE_NODE_CHANGED);
+ EXPECT_EQ(msg->direction, (int32_t)dir);
+ EXPECT_EQ(msg->node_id, node_id);
+}
+
+TEST_F(RClientMessagesSuite, SendOutputNodeVolumeChanged) {
+ void *void_client = reinterpret_cast<void *>(rclient_);
+ char buf[1024];
+ ssize_t rc;
+ struct cras_client_node_value_changed *msg =
+ (struct cras_client_node_value_changed *)buf;
+ const cras_node_id_t node_id = 0x0001000200030004;
+ const int32_t value = 90;
+
+ send_output_node_volume_changed(void_client, node_id, value);
+ rc = read(pipe_fds_[0], buf, sizeof(buf));
+ ASSERT_EQ(rc, (ssize_t)sizeof(*msg));
+ EXPECT_EQ(msg->header.id, CRAS_CLIENT_OUTPUT_NODE_VOLUME_CHANGED);
+ EXPECT_EQ(msg->node_id, node_id);
+ EXPECT_EQ(msg->value, value);
+}
+
+TEST_F(RClientMessagesSuite, SendNodeLeftRightSwappedChanged) {
+ void *void_client = reinterpret_cast<void *>(rclient_);
+ char buf[1024];
+ ssize_t rc;
+ struct cras_client_node_value_changed *msg =
+ (struct cras_client_node_value_changed *)buf;
+ const cras_node_id_t node_id = 0x0001000200030004;
+ const int32_t value = 0;
+
+ send_node_left_right_swapped_changed(void_client, node_id, value);
+ rc = read(pipe_fds_[0], buf, sizeof(buf));
+ ASSERT_EQ(rc, (ssize_t)sizeof(*msg));
+ EXPECT_EQ(msg->header.id, CRAS_CLIENT_NODE_LEFT_RIGHT_SWAPPED_CHANGED);
+ EXPECT_EQ(msg->node_id, node_id);
+ EXPECT_EQ(msg->value, value);
+}
+
+TEST_F(RClientMessagesSuite, SendNodeInputNodeGainChanged) {
+ void *void_client = reinterpret_cast<void *>(rclient_);
+ char buf[1024];
+ ssize_t rc;
+ struct cras_client_node_value_changed *msg =
+ (struct cras_client_node_value_changed *)buf;
+ const cras_node_id_t node_id = 0x0001000200030004;
+ const int32_t value = -19;
+
+ send_input_node_gain_changed(void_client, node_id, value);
+ rc = read(pipe_fds_[0], buf, sizeof(buf));
+ ASSERT_EQ(rc, (ssize_t)sizeof(*msg));
+ EXPECT_EQ(msg->header.id, CRAS_CLIENT_INPUT_NODE_GAIN_CHANGED);
+ EXPECT_EQ(msg->node_id, node_id);
+ EXPECT_EQ(msg->value, value);
+}
+
+TEST_F(RClientMessagesSuite, SendNumActiveStreamsChanged) {
+ void *void_client = reinterpret_cast<void *>(rclient_);
+ char buf[1024];
+ ssize_t rc;
+ struct cras_client_num_active_streams_changed *msg =
+ (struct cras_client_num_active_streams_changed *)buf;
+ const enum CRAS_STREAM_DIRECTION dir = CRAS_STREAM_INPUT;
+ const uint32_t num_active_streams = 3;
+
+ send_num_active_streams_changed(void_client, dir, num_active_streams);
+ rc = read(pipe_fds_[0], buf, sizeof(buf));
+ ASSERT_EQ(rc, (ssize_t)sizeof(*msg));
+ EXPECT_EQ(msg->header.id, CRAS_CLIENT_NUM_ACTIVE_STREAMS_CHANGED);
+ EXPECT_EQ(msg->direction, (int32_t)dir);
+ EXPECT_EQ(msg->num_active_streams, num_active_streams);
+}
+
} // namespace
int main(int argc, char **argv) {
@@ -345,9 +669,16 @@ int audio_thread_resume(struct audio_thread *thread)
return 0;
}
+int audio_thread_config_global_remix(struct audio_thread *thread,
+ unsigned int num_channels,
+ const float *coefficient)
+{
+ return 0;
+}
+
const char *cras_config_get_socket_file_dir()
{
- return "/tmp";
+ return CRAS_UT_TMPDIR;
}
int cras_rstream_create(struct cras_rstream_config *stream_config,
@@ -434,7 +765,7 @@ struct cras_server_state *cras_system_state_get_no_lock()
return NULL;
}
-key_t cras_sys_state_shm_key()
+key_t cras_sys_state_shm_fd()
{
return 1;
}
@@ -447,8 +778,8 @@ void cras_dsp_dump_info()
{
}
-int cras_iodev_list_set_node_attr(int dev_index, int node_index,
- enum ionode_attr attr, int value)
+int cras_iodev_list_set_node_attr(cras_node_id_t id,
+ enum ionode_attr attr, int value)
{
return 0;
}
@@ -473,6 +804,12 @@ void cras_iodev_list_test_dev_command(unsigned int iodev_idx,
const uint8_t *data) {
}
+void cras_iodev_list_configure_global_remix_converter(
+ unsigned int num_channels,
+ const float *coefficient)
+{
+}
+
int stream_list_add(struct stream_list *list,
struct cras_rstream_config *config,
struct cras_rstream **stream)
@@ -492,17 +829,69 @@ int stream_list_add(struct stream_list *list,
return ret;
}
-struct cras_rstream *stream_list_rm(struct stream_list *list,
- cras_stream_id_t id)
+int stream_list_rm(struct stream_list *list, cras_stream_id_t id)
{
stream_list_disconnect_stream_called++;
- return NULL;
+ return 0;
}
-struct cras_rstream *stream_list_rm_all_client_streams(
- struct stream_list *list, struct cras_rclient *rclient)
+int stream_list_rm_all_client_streams(struct stream_list *list,
+ struct cras_rclient *rclient)
+{
+ return 0;
+}
+
+int cras_send_with_fds(int sockfd, const void *buf, size_t len, int *fd,
+ unsigned int num_fds)
+{
+ return write(sockfd, buf, len);
+}
+
+char *cras_iodev_list_get_hotword_models(cras_node_id_t node_id)
{
return NULL;
}
+int cras_iodev_list_set_hotword_model(cras_node_id_t id,
+ const char *model_name)
+{
+ return 0;
+}
+
+struct cras_observer_client *cras_observer_add(
+ const struct cras_observer_ops *ops,
+ void *context)
+{
+ cras_observer_add_called++;
+ cras_observer_add_context_value = context;
+ memcpy(&cras_observer_ops_value, ops, sizeof(cras_observer_ops_value));
+ return cras_observer_add_return_value;
+}
+
+void cras_observer_get_ops(const struct cras_observer_client *client,
+ struct cras_observer_ops *ops)
+{
+ cras_observer_get_ops_called++;
+ memcpy(ops, &cras_observer_ops_value, sizeof(*ops));
+}
+
+void cras_observer_set_ops(struct cras_observer_client *client,
+ const struct cras_observer_ops *ops)
+{
+ cras_observer_set_ops_called++;
+ memcpy(&cras_observer_ops_value, ops, sizeof(cras_observer_ops_value));
+}
+
+int cras_observer_ops_are_empty(const struct cras_observer_ops *ops)
+{
+ cras_observer_ops_are_empty_called++;
+ return memcmp(&cras_observer_ops_are_empty_empty_ops, ops,
+ sizeof(cras_observer_ops_are_empty_empty_ops)) == 0;
+}
+
+void cras_observer_remove(struct cras_observer_client *client)
+{
+ cras_observer_remove_called++;
+}
+
} // extern "C"
diff --git a/cras/src/tests/rstream_unittest.cc b/cras/src/tests/rstream_unittest.cc
index 4e9496fa..8034bfb6 100644
--- a/cras/src/tests/rstream_unittest.cc
+++ b/cras/src/tests/rstream_unittest.cc
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <fcntl.h>
#include <stdio.h>
-#include <sys/shm.h>
+#include <sys/mman.h>
+#include <sys/types.h>
#include <gtest/gtest.h>
extern "C" {
@@ -53,6 +55,15 @@ TEST_F(RstreamTestSuite, InvalidDirection) {
EXPECT_NE(0, rc);
}
+TEST_F(RstreamTestSuite, InvalidStreamType) {
+ struct cras_rstream *s;
+ int rc;
+
+ config_.stream_type = (enum CRAS_STREAM_TYPE)7;
+ rc = cras_rstream_create(&config_, &s);
+ EXPECT_NE(0, rc);
+}
+
TEST_F(RstreamTestSuite, InvalidBufferSize) {
struct cras_rstream *s;
int rc;
@@ -83,7 +94,7 @@ TEST_F(RstreamTestSuite, CreateOutput) {
struct cras_audio_format fmt_ret;
struct cras_audio_shm *shm_ret;
struct cras_audio_shm shm_mapped;
- int rc, key_ret, shmid;
+ int rc, fd_ret;
size_t shm_size;
rc = cras_rstream_create(&config_, &s);
@@ -101,16 +112,15 @@ TEST_F(RstreamTestSuite, CreateOutput) {
// Check if shm is really set up.
shm_ret = cras_rstream_output_shm(s);
ASSERT_NE((void *)NULL, shm_ret);
- key_ret = cras_rstream_output_shm_key(s);
+ fd_ret = cras_rstream_output_shm_fd(s);
shm_size = cras_rstream_get_total_shm_size(s);
EXPECT_GT(shm_size, 4096);
- shmid = shmget(key_ret, shm_size, 0600);
- EXPECT_GE(shmid, 0);
- shm_mapped.area = (struct cras_audio_shm_area *)shmat(shmid, NULL, 0);
+ shm_mapped.area = (struct cras_audio_shm_area *)mmap(
+ NULL, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_ret, 0);
EXPECT_NE((void *)NULL, shm_mapped.area);
cras_shm_copy_shared_config(&shm_mapped);
EXPECT_EQ(cras_shm_used_size(&shm_mapped), cras_shm_used_size(shm_ret));
- shmdt(shm_mapped.area);
+ munmap(shm_mapped.area, shm_size);
cras_rstream_destroy(s);
}
@@ -120,7 +130,7 @@ TEST_F(RstreamTestSuite, CreateInput) {
struct cras_audio_format fmt_ret;
struct cras_audio_shm *shm_ret;
struct cras_audio_shm shm_mapped;
- int rc, key_ret, shmid;
+ int rc, fd_ret;
size_t shm_size;
config_.direction = CRAS_STREAM_INPUT;
@@ -139,20 +149,50 @@ TEST_F(RstreamTestSuite, CreateInput) {
// Check if shm is really set up.
shm_ret = cras_rstream_input_shm(s);
ASSERT_NE((void *)NULL, shm_ret);
- key_ret = cras_rstream_input_shm_key(s);
+ fd_ret = cras_rstream_input_shm_fd(s);
shm_size = cras_rstream_get_total_shm_size(s);
EXPECT_GT(shm_size, 4096);
- shmid = shmget(key_ret, shm_size, 0600);
- EXPECT_GE(shmid, 0);
- shm_mapped.area = (struct cras_audio_shm_area *)shmat(shmid, NULL, 0);
+ shm_mapped.area = (struct cras_audio_shm_area *)mmap(
+ NULL, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_ret, 0);
EXPECT_NE((void *)NULL, shm_mapped.area);
cras_shm_copy_shared_config(&shm_mapped);
EXPECT_EQ(cras_shm_used_size(&shm_mapped), cras_shm_used_size(shm_ret));
- shmdt(shm_mapped.area);
+ munmap(shm_mapped.area, shm_size);
cras_rstream_destroy(s);
}
+TEST_F(RstreamTestSuite, VerifyStreamTypes) {
+ struct cras_rstream *s;
+ int rc;
+
+ config_.stream_type = CRAS_STREAM_TYPE_DEFAULT;
+ rc = cras_rstream_create(&config_, &s);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(CRAS_STREAM_TYPE_DEFAULT, cras_rstream_get_type(s));
+ EXPECT_NE(CRAS_STREAM_TYPE_MULTIMEDIA, cras_rstream_get_type(s));
+ cras_rstream_destroy(s);
+
+ config_.stream_type = CRAS_STREAM_TYPE_VOICE_COMMUNICATION;
+ rc = cras_rstream_create(&config_, &s);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(CRAS_STREAM_TYPE_VOICE_COMMUNICATION, cras_rstream_get_type(s));
+ cras_rstream_destroy(s);
+
+ config_.direction = CRAS_STREAM_INPUT;
+ config_.stream_type = CRAS_STREAM_TYPE_SPEECH_RECOGNITION;
+ rc = cras_rstream_create(&config_, &s);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(CRAS_STREAM_TYPE_SPEECH_RECOGNITION, cras_rstream_get_type(s));
+ cras_rstream_destroy(s);
+
+ config_.stream_type = CRAS_STREAM_TYPE_PRO_AUDIO;
+ rc = cras_rstream_create(&config_, &s);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(CRAS_STREAM_TYPE_PRO_AUDIO, cras_rstream_get_type(s));
+ cras_rstream_destroy(s);
+}
+
} // namespace
int main(int argc, char **argv) {
@@ -163,12 +203,6 @@ int main(int argc, char **argv) {
/* stubs */
extern "C" {
-int cras_rclient_send_message(const struct cras_rclient *client,
- const struct cras_message *msg)
-{
- return 0;
-}
-
struct cras_audio_area *cras_audio_area_create(int num_channels) {
return NULL;
}
diff --git a/cras/src/tests/server_metrics_unittest.cc b/cras/src/tests/server_metrics_unittest.cc
new file mode 100644
index 00000000..db1b69fd
--- /dev/null
+++ b/cras/src/tests/server_metrics_unittest.cc
@@ -0,0 +1,72 @@
+// Copyright 2016 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "cras_server_metrics.c"
+#include "cras_main_message.h"
+}
+
+static enum CRAS_MAIN_MESSAGE_TYPE type_set;
+static struct cras_server_metrics_message *sent_msg;
+
+void ResetStubData() {
+ type_set = (enum CRAS_MAIN_MESSAGE_TYPE)0;
+}
+
+namespace {
+
+TEST(ServerMetricsTestSuite, Init) {
+ ResetStubData();
+
+ cras_server_metrics_init();
+
+ EXPECT_EQ(type_set, CRAS_MAIN_METRICS);
+}
+
+TEST(ServerMetricsTestSuite, SetMetrics) {
+ ResetStubData();
+ unsigned int delay = 100;
+ sent_msg = (struct cras_server_metrics_message *)calloc(1, sizeof(*sent_msg));
+
+ cras_server_metrics_longest_fetch_delay(delay);
+
+ EXPECT_EQ(sent_msg->header.type, CRAS_MAIN_METRICS);
+ EXPECT_EQ(sent_msg->header.length, sizeof(*sent_msg));
+ EXPECT_EQ(sent_msg->metrics_type, LONGEST_FETCH_DELAY);
+ EXPECT_EQ(sent_msg->data, delay);
+
+ free(sent_msg);
+}
+
+extern "C" {
+
+int cras_main_message_add_handler(enum CRAS_MAIN_MESSAGE_TYPE type,
+ cras_message_callback callback,
+ void *callback_data) {
+ type_set = type;
+ return 0;
+}
+
+void cras_metrics_log_histogram(const char *name, int sample, int min,
+ int max, int nbuckets) {
+}
+
+int cras_main_message_send(struct cras_main_message *msg) {
+ // Copy the sent message so we can examine it in the test later.
+ memcpy(sent_msg, msg, sizeof(*sent_msg));
+ return 0;
+};
+
+} // extern "C"
+} // namespace
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int rc = RUN_ALL_TESTS();
+
+ return rc;
+}
diff --git a/cras/src/tests/shm_unittest.cc b/cras/src/tests/shm_unittest.cc
index e1bdc7dc..c0bec890 100644
--- a/cras/src/tests/shm_unittest.cc
+++ b/cras/src/tests/shm_unittest.cc
@@ -17,7 +17,8 @@ class ShmTestSuite : public testing::Test{
virtual void SetUp() {
memset(&shm_, 0, sizeof(shm_));
shm_.area =
- static_cast<cras_audio_shm_area *>(calloc(1, sizeof(*shm_.area)));
+ static_cast<cras_audio_shm_area *>(
+ calloc(1, sizeof(*shm_.area) + 2048));
cras_shm_set_frame_bytes(&shm_, 4);
cras_shm_set_used_size(&shm_, 1024);
memcpy(&shm_.area->config, &shm_.config, sizeof(shm_.config));
@@ -223,6 +224,33 @@ TEST_F(ShmTestSuite, InvalidReadAndWriteOffset) {
EXPECT_EQ(shm_.config.used_size / 4, frames_);
}
+TEST_F(ShmTestSuite, InputBufferOverrun) {
+ int rc;
+ shm_.area->write_offset[0] = 0;
+ shm_.area->read_offset[0] = 0;
+ shm_.area->write_offset[1] = 0;
+ shm_.area->read_offset[1] = 0;
+
+ shm_.area->write_buf_idx = 0;
+ shm_.area->read_buf_idx = 0;
+
+ EXPECT_EQ(0, cras_shm_num_overruns(&shm_));
+ rc = cras_shm_check_write_overrun(&shm_);
+ EXPECT_EQ(0, rc);
+ cras_shm_buffer_written(&shm_, 100);
+ cras_shm_buffer_write_complete(&shm_);
+
+ rc = cras_shm_check_write_overrun(&shm_);
+ EXPECT_EQ(0, rc);
+ cras_shm_buffer_written(&shm_, 100);
+ cras_shm_buffer_write_complete(&shm_);
+
+ // Assert two consecutive writes causes overrun.
+ rc = cras_shm_check_write_overrun(&shm_);
+ EXPECT_EQ(1, rc);
+ EXPECT_EQ(1, cras_shm_num_overruns(&shm_));
+}
+
} // namespace
int main(int argc, char **argv) {
diff --git a/cras/src/tests/stream_list_unittest.cc b/cras/src/tests/stream_list_unittest.cc
index 64ec0abd..f2e8fbbf 100644
--- a/cras/src/tests/stream_list_unittest.cc
+++ b/cras/src/tests/stream_list_unittest.cc
@@ -1,9 +1,8 @@
-// Copyright (c) 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdio.h>
-#include <sys/shm.h>
#include <gtest/gtest.h>
extern "C" {
diff --git a/cras/src/tests/system_state_unittest.cc b/cras/src/tests/system_state_unittest.cc
index ea3e57d2..5816b771 100644
--- a/cras/src/tests/system_state_unittest.cc
+++ b/cras/src/tests/system_state_unittest.cc
@@ -6,6 +6,7 @@
#include <gtest/gtest.h>
extern "C" {
+#include "cras_alert.h"
#include "cras_system_state.h"
#include "cras_types.h"
}
@@ -27,6 +28,12 @@ static void *rm_callback_arg;
static size_t alert_pending_called;
static char* device_config_dir;
static const char* cras_alsa_card_config_dir;
+static size_t cras_observer_notify_output_volume_called;
+static size_t cras_observer_notify_output_mute_called;
+static size_t cras_observer_notify_capture_gain_called;
+static size_t cras_observer_notify_capture_mute_called;
+static size_t cras_observer_notify_suspend_changed_called;
+static size_t cras_observer_notify_num_active_streams_called;
static void ResetStubData() {
cras_alsa_card_create_called = 0;
@@ -40,27 +47,12 @@ static void ResetStubData() {
alert_pending_called = 0;
device_config_dir = reinterpret_cast<char *>(3);
cras_alsa_card_config_dir = NULL;
-}
-
-static void volume_changed(void *arg) {
-}
-
-static void volume_limits_changed(void *arg) {
-}
-
-static void volume_limits_changed_2(void *arg) {
-}
-
-static void capture_gain_changed(void *arg) {
-}
-
-static void mute_changed(void *arg) {
-}
-
-static void capture_mute_changed(void *arg) {
-}
-
-static void capture_mute_changed_2(void *arg) {
+ cras_observer_notify_output_volume_called = 0;
+ cras_observer_notify_output_mute_called = 0;
+ cras_observer_notify_capture_gain_called = 0;
+ cras_observer_notify_capture_mute_called = 0;
+ cras_observer_notify_suspend_changed_called = 0;
+ cras_observer_notify_num_active_streams_called = 0;
}
static int add_stub(int fd, void (*cb)(void *data),
@@ -99,6 +91,7 @@ TEST(SystemStateSuite, SetVolume) {
cras_system_set_volume(CRAS_MAX_SYSTEM_VOLUME + 1);
EXPECT_EQ(CRAS_MAX_SYSTEM_VOLUME, cras_system_get_volume());
cras_system_state_deinit();
+ EXPECT_EQ(4, cras_observer_notify_output_volume_called);
}
TEST(SystemStateSuite, SetMinMaxVolume) {
@@ -119,241 +112,132 @@ TEST(SystemStateSuite, SetCaptureVolume) {
cras_system_set_capture_gain(-10000);
EXPECT_EQ(-5000, cras_system_get_capture_gain());
cras_system_state_deinit();
+ EXPECT_EQ(3, cras_observer_notify_capture_gain_called);
}
-TEST(SystemStateSuite, VolumeChangedCallback) {
- void * const fake_user_arg = (void *)1;
- const size_t fake_volume = 55;
- const size_t fake_volume_2 = 44;
- int rc;
-
+TEST(SystemStateSuite, SetCaptureVolumeStoreTarget) {
cras_system_state_init(device_config_dir);
- ResetStubData();
- cras_system_register_volume_changed_cb(volume_changed, fake_user_arg);
- EXPECT_EQ(1, add_callback_called);
- EXPECT_EQ((void *)volume_changed, (void *)add_callback_cb);
- EXPECT_EQ(fake_user_arg, add_callback_arg);
+ cras_system_set_capture_gain_limits(-2000, 2000);
+ cras_system_set_capture_gain(3000);
+ // Gain is within the limit.
+ EXPECT_EQ(2000, cras_system_get_capture_gain());
- cras_system_set_volume(fake_volume);
- EXPECT_EQ(fake_volume, cras_system_get_volume());
- EXPECT_EQ(1, alert_pending_called);
+ // Assume the range is changed.
+ cras_system_set_capture_gain_limits(-4000, 4000);
- rc = cras_system_remove_volume_changed_cb(volume_changed, fake_user_arg);
- EXPECT_EQ(0, rc);
- EXPECT_EQ(1, rm_callback_called);
- EXPECT_EQ((void *)volume_changed, (void *)rm_callback_cb);
- EXPECT_EQ(fake_user_arg, rm_callback_arg);
+ // Gain is also changed because target gain is re-applied.
+ EXPECT_EQ(3000, cras_system_get_capture_gain());
- cras_system_set_volume(fake_volume_2);
- EXPECT_EQ(fake_volume_2, cras_system_get_volume());
- EXPECT_EQ(2, alert_pending_called);
cras_system_state_deinit();
}
-TEST(SystemStateSuite, VolumeLimitChangedCallbackMultiple) {
- void * const fake_user_arg = (void *)1;
- void * const fake_user_arg_2 = (void *)2;
- const size_t fake_min = -10000;
- const size_t fake_max = 800;
- const size_t fake_min_2 = -4500;
- const size_t fake_max_2 = -600;
- int rc;
-
+TEST(SystemStateSuite, SetMinMaxCaptureGain) {
cras_system_state_init(device_config_dir);
- ResetStubData();
- rc = cras_system_register_volume_limits_changed_cb(volume_limits_changed,
- fake_user_arg);
- EXPECT_EQ(0, rc);
- EXPECT_EQ(1, add_callback_called);
- EXPECT_EQ((void *)volume_limits_changed, (void *)add_callback_cb);
- EXPECT_EQ(fake_user_arg, add_callback_arg);
-
- cras_system_register_volume_limits_changed_cb(volume_limits_changed_2,
- fake_user_arg_2);
- EXPECT_EQ(2, add_callback_called);
- EXPECT_EQ((void *)volume_limits_changed_2, (void *)add_callback_cb);
- EXPECT_EQ(fake_user_arg_2, add_callback_arg);
-
- cras_system_set_volume_limits(fake_min, fake_max);
- cras_system_set_capture_gain_limits(fake_min_2, fake_max_2);
- EXPECT_EQ(fake_min, cras_system_get_min_volume());
- EXPECT_EQ(fake_max, cras_system_get_max_volume());
- EXPECT_EQ(2, alert_pending_called);
-
- cras_system_remove_volume_limits_changed_cb(volume_limits_changed,
- fake_user_arg);
- EXPECT_EQ(1, rm_callback_called);
- EXPECT_EQ((void *)volume_limits_changed, (void *)rm_callback_cb);
- EXPECT_EQ(fake_user_arg, rm_callback_arg);
-
- cras_system_set_volume_limits(fake_min_2, fake_max_2);
- EXPECT_EQ(fake_min_2, cras_system_get_min_volume());
- EXPECT_EQ(fake_max_2, cras_system_get_max_volume());
- EXPECT_EQ(3, alert_pending_called);
-
- cras_system_remove_volume_limits_changed_cb(volume_limits_changed_2,
- fake_user_arg_2);
-
- cras_system_set_volume_limits(fake_min, fake_max);
- EXPECT_EQ(fake_min, cras_system_get_min_volume());
- EXPECT_EQ(fake_max, cras_system_get_max_volume());
- EXPECT_EQ(4, alert_pending_called);
+ cras_system_set_capture_gain(3000);
+ cras_system_set_capture_gain_limits(-2000, 2000);
+ EXPECT_EQ(-2000, cras_system_get_min_capture_gain());
+ EXPECT_EQ(2000, cras_system_get_max_capture_gain());
+ // Current gain is adjusted for range.
+ EXPECT_EQ(2000, cras_system_get_capture_gain());
cras_system_state_deinit();
}
-TEST(SystemStateSuite, CaptureVolumeChangedCallback) {
- void * const fake_user_arg = (void *)1;
- const long fake_capture_gain = 2200;
- const long fake_capture_gain_2 = -1600;
- int rc;
-
- cras_system_state_init(device_config_dir);
+TEST(SystemStateSuite, SetUserMute) {
ResetStubData();
- cras_system_register_capture_gain_changed_cb(capture_gain_changed,
- fake_user_arg);
- EXPECT_EQ(1, add_callback_called);
- EXPECT_EQ((void *)capture_gain_changed, (void *)add_callback_cb);
- EXPECT_EQ(fake_user_arg, add_callback_arg);
-
- cras_system_set_capture_gain(fake_capture_gain);
- EXPECT_EQ(fake_capture_gain, cras_system_get_capture_gain());
- EXPECT_EQ(1, alert_pending_called);
-
- rc = cras_system_remove_capture_gain_changed_cb(capture_gain_changed,
- fake_user_arg);
- EXPECT_EQ(0, rc);
- EXPECT_EQ(1, rm_callback_called);
- EXPECT_EQ((void *)capture_gain_changed, (void *)rm_callback_cb);
- EXPECT_EQ(fake_user_arg, rm_callback_arg);
-
- cras_system_set_capture_gain(fake_capture_gain_2);
- EXPECT_EQ(fake_capture_gain_2, cras_system_get_capture_gain());
- EXPECT_EQ(2, alert_pending_called);
- cras_system_state_deinit();
-}
-
-TEST(SystemStateSuite, SetMute) {
cras_system_state_init(device_config_dir);
+
EXPECT_EQ(0, cras_system_get_mute());
- cras_system_set_mute(0);
+
+ cras_system_set_user_mute(0);
EXPECT_EQ(0, cras_system_get_mute());
- cras_system_set_mute(1);
+ EXPECT_EQ(0, cras_observer_notify_output_mute_called);
+
+ cras_system_set_user_mute(1);
EXPECT_EQ(1, cras_system_get_mute());
- cras_system_set_mute(22);
+ EXPECT_EQ(1, cras_observer_notify_output_mute_called);
+
+ cras_system_set_user_mute(22);
EXPECT_EQ(1, cras_system_get_mute());
+ EXPECT_EQ(1, cras_observer_notify_output_mute_called);
+
cras_system_state_deinit();
}
-TEST(SystemStateSuite, MuteChangedCallback) {
- void * const fake_user_arg = (void *)1;
- int rc;
-
- cras_system_state_init(device_config_dir);
+TEST(SystemStateSuite, SetMute) {
ResetStubData();
- cras_system_register_mute_changed_cb(mute_changed, fake_user_arg);
- EXPECT_EQ(1, add_callback_called);
- EXPECT_EQ((void *)mute_changed, (void *)add_callback_cb);
- EXPECT_EQ(fake_user_arg, add_callback_arg);
+ cras_system_state_init(device_config_dir);
+
+ EXPECT_EQ(0, cras_system_get_mute());
+
+ cras_system_set_mute(0);
+ EXPECT_EQ(0, cras_system_get_mute());
+ EXPECT_EQ(0, cras_observer_notify_output_mute_called);
cras_system_set_mute(1);
EXPECT_EQ(1, cras_system_get_mute());
- EXPECT_EQ(1, alert_pending_called);
+ EXPECT_EQ(1, cras_observer_notify_output_mute_called);
- rc = cras_system_remove_mute_changed_cb(mute_changed, fake_user_arg);
- EXPECT_EQ(0, rc);
- EXPECT_EQ(1, rm_callback_called);
- EXPECT_EQ((void *)mute_changed, (void *)rm_callback_cb);
- EXPECT_EQ(fake_user_arg, rm_callback_arg);
+ cras_system_set_mute(22);
+ EXPECT_EQ(1, cras_system_get_mute());
+ EXPECT_EQ(1, cras_observer_notify_output_mute_called);
- cras_system_set_mute(0);
- EXPECT_EQ(0, cras_system_get_mute());
- EXPECT_EQ(2, alert_pending_called);
cras_system_state_deinit();
}
TEST(SystemStateSuite, CaptureMuteChangedCallbackMultiple) {
- void * const fake_arg = (void *)1;
- void * const fake_arg_2 = (void *)2;
- int rc;
-
cras_system_state_init(device_config_dir);
ResetStubData();
- rc = cras_system_register_capture_mute_changed_cb(capture_mute_changed,
- fake_arg);
- EXPECT_EQ(0, rc);
- EXPECT_EQ(1, add_callback_called);
- EXPECT_EQ((void *)capture_mute_changed, (void *)add_callback_cb);
- EXPECT_EQ(fake_arg, add_callback_arg);
-
- rc = cras_system_register_capture_mute_changed_cb(capture_mute_changed_2,
- fake_arg_2);
- EXPECT_EQ(0, rc);
- EXPECT_EQ(2, add_callback_called);
- EXPECT_EQ((void *)capture_mute_changed_2, (void *)add_callback_cb);
- EXPECT_EQ(fake_arg_2, add_callback_arg);
cras_system_set_capture_mute(1);
EXPECT_EQ(1, cras_system_get_capture_mute());
- EXPECT_EQ(1, alert_pending_called);
-
- rc = cras_system_remove_capture_mute_changed_cb(capture_mute_changed,
- fake_arg);
- EXPECT_EQ(0, rc);
- EXPECT_EQ(1, rm_callback_called);
- EXPECT_EQ((void *)capture_mute_changed, (void *)rm_callback_cb);
- EXPECT_EQ(fake_arg, rm_callback_arg);
-
+ EXPECT_EQ(1, cras_observer_notify_capture_mute_called);
cras_system_set_capture_mute(0);
EXPECT_EQ(0, cras_system_get_capture_mute());
- EXPECT_EQ(2, alert_pending_called);
+ EXPECT_EQ(2, cras_observer_notify_capture_mute_called);
- rc = cras_system_remove_capture_mute_changed_cb(capture_mute_changed_2,
- fake_arg_2);
- EXPECT_EQ(0, rc);
cras_system_state_deinit();
}
TEST(SystemStateSuite, MuteLocked) {
- void * const fake_user_arg = (void *)1;
- int rc;
-
cras_system_state_init(device_config_dir);
ResetStubData();
- cras_system_register_mute_changed_cb(mute_changed, fake_user_arg);
- EXPECT_EQ(1, add_callback_called);
- EXPECT_EQ((void *)mute_changed, (void *)add_callback_cb);
- EXPECT_EQ(fake_user_arg, add_callback_arg);
-
cras_system_set_mute(1);
EXPECT_EQ(1, cras_system_get_mute());
EXPECT_EQ(0, cras_system_get_mute_locked());
- EXPECT_EQ(1, alert_pending_called);
+ EXPECT_EQ(1, cras_observer_notify_output_mute_called);
cras_system_set_mute_locked(1);
cras_system_set_mute(0);
EXPECT_EQ(1, cras_system_get_mute());
EXPECT_EQ(1, cras_system_get_mute_locked());
- EXPECT_EQ(1, alert_pending_called);
-
- rc = cras_system_remove_mute_changed_cb(mute_changed, fake_user_arg);
- EXPECT_EQ(0, rc);
- EXPECT_EQ(1, rm_callback_called);
- EXPECT_EQ((void *)mute_changed, (void *)rm_callback_cb);
- EXPECT_EQ(fake_user_arg, rm_callback_arg);
+ EXPECT_EQ(2, cras_observer_notify_output_mute_called);
- cras_system_register_capture_mute_changed_cb(capture_mute_changed,
- fake_user_arg);
cras_system_set_capture_mute(1);
EXPECT_EQ(1, cras_system_get_capture_mute());
EXPECT_EQ(0, cras_system_get_capture_mute_locked());
- EXPECT_EQ(2, alert_pending_called);
+ EXPECT_EQ(1, cras_observer_notify_capture_mute_called);
cras_system_set_capture_mute_locked(1);
cras_system_set_capture_mute(0);
EXPECT_EQ(1, cras_system_get_capture_mute());
EXPECT_EQ(1, cras_system_get_capture_mute_locked());
- EXPECT_EQ(2, alert_pending_called);
+ cras_system_state_deinit();
+ EXPECT_EQ(2, cras_observer_notify_capture_mute_called);
+}
+
+TEST(SystemStateSuite, Suspend) {
+ cras_system_state_init(device_config_dir);
+ ResetStubData();
+
+ cras_system_set_suspended(1);
+ EXPECT_EQ(1, cras_observer_notify_suspend_changed_called);
+ EXPECT_EQ(1, cras_system_get_suspended());
+
+ cras_system_set_suspended(0);
+ EXPECT_EQ(2, cras_observer_notify_suspend_changed_called);
+ EXPECT_EQ(0, cras_system_get_suspended());
+
cras_system_state_deinit();
}
@@ -368,6 +252,7 @@ TEST(SystemStateSuite, AddCardFailCreate) {
EXPECT_EQ(-ENOMEM, cras_system_add_alsa_card(&info));
EXPECT_EQ(1, cras_alsa_card_create_called);
EXPECT_EQ(cras_alsa_card_config_dir, device_config_dir);
+ cras_system_state_deinit();
}
TEST(SystemStateSuite, AddCard) {
@@ -387,6 +272,7 @@ TEST(SystemStateSuite, AddCard) {
// Removing card should destroy it.
cras_system_remove_alsa_card(0);
EXPECT_EQ(1, cras_alsa_card_destroy_called);
+ cras_system_state_deinit();
}
TEST(SystemSettingsRegisterSelectDescriptor, AddSelectFd) {
@@ -454,6 +340,7 @@ TEST(SystemSettingsStreamCount, StreamCountByDirection) {
cras_system_state_get_active_streams_by_direction(
CRAS_STREAM_POST_MIX_PRE_DSP));
EXPECT_EQ(3, cras_system_state_get_active_streams());
+ EXPECT_EQ(3, cras_observer_notify_num_active_streams_called);
cras_system_state_stream_removed(CRAS_STREAM_OUTPUT);
cras_system_state_stream_removed(CRAS_STREAM_INPUT);
cras_system_state_stream_removed(CRAS_STREAM_POST_MIX_PRE_DSP);
@@ -467,6 +354,7 @@ TEST(SystemSettingsStreamCount, StreamCountByDirection) {
cras_system_state_get_active_streams_by_direction(
CRAS_STREAM_POST_MIX_PRE_DSP));
EXPECT_EQ(0, cras_system_state_get_active_streams());
+ EXPECT_EQ(6, cras_observer_notify_num_active_streams_called);
cras_system_state_deinit();
}
@@ -500,7 +388,8 @@ void cras_device_blacklist_destroy(struct cras_device_blacklist *blacklist)
{
}
-struct cras_alert *cras_alert_create(cras_alert_prepare prepare)
+struct cras_alert *cras_alert_create(cras_alert_prepare prepare,
+ unsigned int flags)
{
return NULL;
}
@@ -540,6 +429,38 @@ void cras_tm_deinit(cras_tm *tm) {
free(tm);
}
+void cras_observer_notify_output_volume(int32_t volume)
+{
+ cras_observer_notify_output_volume_called++;
+}
+
+void cras_observer_notify_output_mute(int muted, int user_muted,
+ int mute_locked)
+{
+ cras_observer_notify_output_mute_called++;
+}
+
+void cras_observer_notify_capture_gain(int32_t gain)
+{
+ cras_observer_notify_capture_gain_called++;
+}
+
+void cras_observer_notify_capture_mute(int muted, int mute_locked)
+{
+ cras_observer_notify_capture_mute_called++;
+}
+
+void cras_observer_notify_suspend_changed(int suspended)
+{
+ cras_observer_notify_suspend_changed_called++;
+}
+
+void cras_observer_notify_num_active_streams(enum CRAS_STREAM_DIRECTION dir,
+ uint32_t num_active_streams)
+{
+ cras_observer_notify_num_active_streams_called++;
+}
+
} // extern "C"
} // namespace
diff --git a/cras/src/tests/utf8_unittest.cc b/cras/src/tests/utf8_unittest.cc
new file mode 100644
index 00000000..34360c36
--- /dev/null
+++ b/cras/src/tests/utf8_unittest.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2012 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.
+
+// Some UTF character seqeuences in this file were taken from
+// https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+
+#include <gtest/gtest.h>
+#include <stdio.h>
+
+extern "C" {
+#include "cras_utf8.h"
+}
+
+namespace {
+
+TEST(UTF8, ValidStress) {
+ size_t pos;
+
+ EXPECT_EQ(1, valid_utf8_string("The greek word 'kosme': "
+ "\xce\xba\xe1\xbd\xb9\xcf\x83\xce"
+ "\xbc\xce\xb5", &pos));
+ EXPECT_EQ(35, pos);
+
+ EXPECT_EQ(1, valid_utf8_string("Playback", &pos));
+ EXPECT_EQ(8, pos);
+
+ EXPECT_EQ(1, valid_utf8_string("The Euro sign: \xe2\x82\xac", &pos));
+ EXPECT_EQ(18, pos);
+
+ /* First possible sequence of a certain length. */
+ EXPECT_EQ(1, valid_utf8_string("\x01", &pos));
+ EXPECT_EQ(1, pos);
+ EXPECT_EQ(1, valid_utf8_string("\xc2\x80", &pos));
+ EXPECT_EQ(2, pos);
+ EXPECT_EQ(1, valid_utf8_string("\xe0\xa0\x80", &pos));
+ EXPECT_EQ(3, pos);
+ EXPECT_EQ(1, valid_utf8_string("\xe1\x80\x80", &pos));
+ EXPECT_EQ(3, pos);
+ EXPECT_EQ(1, valid_utf8_string("\xf0\x90\x80\x80", &pos));
+ EXPECT_EQ(4, pos);
+ EXPECT_EQ(1, valid_utf8_string("\xf1\x80\x80\x80", &pos));
+ EXPECT_EQ(4, pos);
+
+ /* Last possible sequence of a certain length. */
+ EXPECT_EQ(1, valid_utf8_string("\x7f", &pos));
+ EXPECT_EQ(1, pos);
+ EXPECT_EQ(1, valid_utf8_string("\xdf\xbf", &pos));
+ EXPECT_EQ(2, pos);
+ EXPECT_EQ(1, valid_utf8_string("\xef\xbf\xbf", &pos));
+ EXPECT_EQ(3, pos);
+ EXPECT_EQ(1, valid_utf8_string("\xf4\x8f\xbf\xbf", &pos));
+ EXPECT_EQ(4, pos);
+
+ /* Other boundary conditions. */
+ EXPECT_EQ(1, valid_utf8_string("\xed\x9f\xbf", &pos));
+ EXPECT_EQ(3, pos);
+ EXPECT_EQ(1, valid_utf8_string("\xee\x80\x80", &pos));
+ EXPECT_EQ(3, pos);
+ EXPECT_EQ(1, valid_utf8_string("\xef\xbf\xbd", &pos));
+ EXPECT_EQ(3, pos);
+ EXPECT_EQ(1, valid_utf8_string("\xf0\xbf\xbf\xbf", &pos));
+ EXPECT_EQ(4, pos);
+
+ /* BOM sequence. */
+ EXPECT_EQ(1, valid_utf8_string("\xef\xbb\xbf", &pos));
+ EXPECT_EQ(3, pos);
+
+ /* Valid UTF-8 that shouldn't appear in text; chose to allow
+ * these characters anyway. */
+ EXPECT_EQ(1, valid_utf8_string("U+FFFE: \xef\xbf\xbe", &pos));
+ EXPECT_EQ(11, pos);
+ EXPECT_EQ(1, valid_utf8_string("U+FDD0: \xef\xb7\x90", &pos));
+ EXPECT_EQ(11, pos);
+ EXPECT_EQ(1, valid_utf8_string("\xf0\x9f\xbf\xbe", &pos));
+ EXPECT_EQ(4, pos);
+}
+
+TEST(UTF8, InvalidStress) {
+ size_t pos;
+
+ /* Malformed continuation bytes. */
+ EXPECT_EQ(0, valid_utf8_string("\x80", &pos));
+ EXPECT_EQ(0, pos);
+ EXPECT_EQ(0, valid_utf8_string("\xbf", &pos));
+ EXPECT_EQ(0, pos);
+ EXPECT_EQ(0, valid_utf8_string("\x80\xbf", &pos));
+ EXPECT_EQ(0, pos);
+ EXPECT_EQ(0, valid_utf8_string("\xc2\x80\xbf", &pos));
+ EXPECT_EQ(2, pos);
+
+ /* Lonely start characters. */
+ EXPECT_EQ(0, valid_utf8_string("\xc2 \xc3 \xc4 ", &pos));
+ EXPECT_EQ(1, pos);
+
+ /* Out of range cases. */
+ EXPECT_EQ(0, valid_utf8_string("\xf4\x90\xbf\xbf", &pos));
+ EXPECT_EQ(1, pos);
+ EXPECT_EQ(0, valid_utf8_string(" \xf5\x80", &pos));
+ EXPECT_EQ(1, pos);
+ EXPECT_EQ(0, valid_utf8_string(" \xe0\x80\x80", &pos));
+ EXPECT_EQ(2, pos);
+ EXPECT_EQ(0, valid_utf8_string("\xf4\x80\x80\xcf", &pos));
+ EXPECT_EQ(3, pos);
+
+ /* Stop in mid-sequence. */
+ EXPECT_EQ(0, valid_utf8_string("\xf4\x80", &pos));
+ EXPECT_EQ(2, pos);
+
+ /* Bad characters. */
+ EXPECT_EQ(0, valid_utf8_string("\xff", &pos));
+ EXPECT_EQ(0, pos);
+ EXPECT_EQ(0, valid_utf8_string("\xfe", &pos));
+ EXPECT_EQ(0, pos);
+
+ /* Overlong representations of ASCII characters. */
+ EXPECT_EQ(0, valid_utf8_string("This represents the / character with too"
+ "many bytes: \xe0\x80\xaf", &pos));
+ EXPECT_EQ(53, pos);
+ EXPECT_EQ(0, valid_utf8_string("This represents the / character with too"
+ "many bytes: \xf0\x80\x80\xaf", &pos));
+ EXPECT_EQ(53, pos);
+
+ /* Should not be interpreted as the ASCII NUL character. */
+ EXPECT_EQ(0, valid_utf8_string("This represents the NUL character with too"
+ "many bytes: \xe0\x80\x80", &pos));
+ EXPECT_EQ(55, pos);
+ EXPECT_EQ(0, valid_utf8_string("This represents the NUL character with too"
+ "many bytes: \xf0\x80\x80\x80", &pos));
+ EXPECT_EQ(55, pos);
+
+ /* Single UTF-16 surrogates. */
+ EXPECT_EQ(0, valid_utf8_string("\xed\xa0\x80", &pos));
+ EXPECT_EQ(1, pos);
+ EXPECT_EQ(0, valid_utf8_string("\xed\xad\xbf", &pos));
+ EXPECT_EQ(1, pos);
+ EXPECT_EQ(0, valid_utf8_string("\xed\xae\x80", &pos));
+ EXPECT_EQ(1, pos);
+ EXPECT_EQ(0, valid_utf8_string("\xed\xaf\xbf", &pos));
+ EXPECT_EQ(1, pos);
+ EXPECT_EQ(0, valid_utf8_string("\xed\xb0\x80", &pos));
+ EXPECT_EQ(1, pos);
+ EXPECT_EQ(0, valid_utf8_string("\xed\xbe\x80", &pos));
+ EXPECT_EQ(1, pos);
+ EXPECT_EQ(0, valid_utf8_string("\xed\xbf\xbf", &pos));
+ EXPECT_EQ(1, pos);
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/cras/src/tests/util_unittest.cc b/cras/src/tests/util_unittest.cc
index 0c85dec3..3dbc026f 100644
--- a/cras/src/tests/util_unittest.cc
+++ b/cras/src/tests/util_unittest.cc
@@ -3,21 +3,110 @@
// found in the LICENSE file.
#include <gtest/gtest.h>
+#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
+#include <vector>
#include "cras_util.h"
namespace {
-static struct timespec time_now;
+static std::vector<struct timespec> time_now;
+
+TEST(Util, SendRecvTwoFileDescriptors) {
+ int fd[2];
+ int fd2[2];
+ int send_fds[2];
+ int sock[2];
+ char buf[256] = {0};
+ int new_fds[2];
+ char msg[] = "multi-fd";
+ unsigned int num_fds = 2;
+
+ /* Create a pipe and a pair of sockets. Then send the write end of
+ * the pipe (fd[1]) through the socket, and receive it as
+ * new_fd */
+ ASSERT_EQ(0, pipe(fd));
+ ASSERT_EQ(0, pipe(fd2));
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sock));
+
+ send_fds[0] = fd[1];
+ send_fds[1] = fd2[1];
+ ASSERT_GE(cras_send_with_fds(sock[0], msg, strlen(msg), send_fds, num_fds),
+ 0);
+ ASSERT_GE(cras_recv_with_fds(sock[1], buf, strlen(msg), new_fds, &num_fds),
+ 0);
+ ASSERT_STREQ(msg, buf);
+ ASSERT_EQ(2, num_fds);
+ ASSERT_NE(-1, new_fds[0]);
+ ASSERT_NE(-1, new_fds[1]);
+
+ close(sock[0]);
+ close(sock[1]);
+ close(fd[1]);
+ close(fd2[1]);
+
+ /* Send a character to the new_fd, and receive it from the read end
+ * of the pipe (fd[0]) */
+ ASSERT_EQ(1, write(new_fds[0], "a", 1));
+ ASSERT_EQ(1, read(fd[0], buf, 1));
+ ASSERT_EQ('a', buf[0]);
+ ASSERT_EQ(1, write(new_fds[1], "b", 1));
+ ASSERT_EQ(1, read(fd2[0], buf, 1));
+ ASSERT_EQ('b', buf[0]);
+
+ close(fd[0]);
+ close(fd2[0]);
+ close(new_fds[0]);
+ close(new_fds[1]);
+}
+
+TEST(Util, SendOneRecvTwoFileDescriptors) {
+ int fd[2];
+ int sock[2];
+ char buf[256] = {0};
+ int new_fds[2];
+ char msg[] = "multi-fd";
+ unsigned int num_fds = 2;
+
+ /* Create a pipe and a pair of sockets. Then send the write end of
+ * the pipe (fd[1]) through the socket, and receive it as
+ * new_fd */
+ ASSERT_EQ(0, pipe(fd));
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sock));
+
+ ASSERT_GE(cras_send_with_fds(sock[0], msg, strlen(msg), &fd[1], 1), 0);
+ ASSERT_GE(cras_recv_with_fds(sock[1], buf, strlen(msg), new_fds, &num_fds),
+ 0);
+ ASSERT_STREQ(msg, buf);
+ ASSERT_EQ(1, num_fds);
+ ASSERT_NE(-1, new_fds[0]);
+ ASSERT_EQ(-1, new_fds[1]);
+
+ close(sock[0]);
+ close(sock[1]);
+ close(fd[1]);
+
+ /* Send a character to the new_fd, and receive it from the read end
+ * of the pipe (fd[0]) */
+ ASSERT_EQ(1, write(new_fds[0], "a", 1));
+ ASSERT_EQ(1, read(fd[0], buf, 1));
+ ASSERT_EQ('a', buf[0]);
+
+ close(fd[0]);
+ close(new_fds[0]);
+ close(new_fds[1]);
+}
TEST(Util, SendRecvFileDescriptor) {
int fd[2];
int sock[2];
- char buf[6] = {0};
+ char buf[256] = {0};
int new_fd;
+ char msg[] = "hello";
+ unsigned int num_fds = 1;
/* Create a pipe and a pair of sockets. Then send the write end of
* the pipe (fd[1]) through the socket, and receive it as
@@ -25,9 +114,11 @@ TEST(Util, SendRecvFileDescriptor) {
ASSERT_EQ(0, pipe(fd));
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sock));
- ASSERT_EQ(5, cras_send_with_fd(sock[0], "hello", 5, fd[1]));
- ASSERT_EQ(5, cras_recv_with_fd(sock[1], buf, 5, &new_fd));
- ASSERT_STREQ("hello", buf);
+ ASSERT_EQ(5, cras_send_with_fds(sock[0], msg, strlen(msg), &fd[1], num_fds));
+ ASSERT_EQ(5,
+ cras_recv_with_fds(sock[1], buf, strlen(msg), &new_fd, &num_fds));
+ ASSERT_STREQ(msg, buf);
+ ASSERT_EQ(1, num_fds);
close(sock[0]);
close(sock[1]);
@@ -43,6 +134,23 @@ TEST(Util, SendRecvFileDescriptor) {
close(new_fd);
}
+TEST(Util, SendRecvNoDescriptors) {
+ char buf[256] = {0};
+ char msg[] = "no descriptors";
+ unsigned int num_fds = 0;
+ int sock[2];
+
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sock));
+
+ ASSERT_GE(cras_send_with_fds(sock[0], msg, strlen(msg), NULL, num_fds), 0);
+ ASSERT_GE(cras_recv_with_fds(sock[1], buf, strlen(msg), NULL, &num_fds), 0);
+ ASSERT_STREQ(msg, buf);
+ ASSERT_EQ(0, num_fds);
+
+ close(sock[0]);
+ close(sock[1]);
+}
+
TEST(Util, TimevalAfter) {
struct timeval t0, t1;
t0.tv_sec = 0;
@@ -137,28 +245,79 @@ TEST(Util, TimespecToMs) {
}
TEST(Util, FramesSinceTime) {
- struct timespec t;
+ struct timespec t, tn;
unsigned int frames;
t.tv_sec = 0;
t.tv_nsec = 500000000;
- time_now.tv_sec = 2;
- time_now.tv_nsec = 0;
+ tn.tv_sec = 2;
+ tn.tv_nsec = 0;
+ time_now.push_back(tn);
frames = cras_frames_since_time(&t, 48000);
EXPECT_EQ(72000, frames);
- time_now.tv_sec = 0;
- time_now.tv_nsec = 0;
+ tn.tv_sec = 0;
+ time_now.push_back(tn);
frames = cras_frames_since_time(&t, 48000);
EXPECT_EQ(0, frames);
}
+// Test cras_poll().
+TEST(Util, CrasPoll) {
+ int pipe_fds[2];
+ struct pollfd poll_fd;
+ std::string output;
+ struct timespec timeout;
+ char buf[256];
+
+ ASSERT_EQ(0, pipe(pipe_fds));
+ poll_fd.fd = pipe_fds[0];
+ poll_fd.events = POLLIN;
+ ASSERT_NE(0, poll_fd.fd >= 0);
+
+ // Simple poll.
+ output = "Hello";
+ EXPECT_EQ(output.size() + 1,
+ write(pipe_fds[1], output.c_str(), output.size() + 1));
+ EXPECT_EQ(1, cras_poll(&poll_fd, 1, NULL, NULL));
+ ASSERT_EQ(static_cast<ssize_t>(output.size() + 1),
+ read(pipe_fds[0], buf, sizeof(buf)));
+ EXPECT_EQ(0, strcmp(output.c_str(), buf));
+
+ // Negative time.
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = -10000000;
+ EXPECT_EQ(-ETIMEDOUT, cras_poll(&poll_fd, 1, &timeout, NULL));
+ timeout.tv_sec = -1;
+ timeout.tv_nsec = 10000000;
+ EXPECT_EQ(-ETIMEDOUT, cras_poll(&poll_fd, 1, &timeout, NULL));
+
+ // Timeout.
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ time_now.push_back(timeout);
+ timeout.tv_nsec = 1100000;
+ time_now.push_back(timeout);
+ timeout.tv_nsec = 1000000;
+ EXPECT_EQ(-ETIMEDOUT, cras_poll(&poll_fd, 1, &timeout, NULL));
+ EXPECT_EQ(timeout.tv_nsec, -100000);
+
+ EXPECT_EQ(0, close(pipe_fds[0]));
+ EXPECT_EQ(0, close(pipe_fds[1]));
+}
+
/* Stubs */
extern "C" {
int clock_gettime(clockid_t clk_id, struct timespec *tp) {
- *tp = time_now;
+ std::vector<struct timespec>::iterator i = time_now.begin();
+ if (i != time_now.end()) {
+ *tp = *i;
+ time_now.erase(i);
+ }
+ else
+ memset(tp, 0, sizeof(*tp));
return 0;
}
diff --git a/defs/utilities.mk b/defs/utilities.mk
index 230eae5c..8bc4c046 100644
--- a/defs/utilities.mk
+++ b/defs/utilities.mk
@@ -15,3 +15,4 @@ export AR = /usr/bin/ar
export ECHO = /bin/echo
export MESSAGE = $(ECHO) "$(foreach v,$(shell seq $(MAKELEVEL))," ") [$(MAKELEVEL)] "
export INSTALL = /usr/bin/install
+export LINK = /bin/ln
diff --git a/init/cras-directories.conf b/init/cras-directories.conf
new file mode 100644
index 00000000..4a79a186
--- /dev/null
+++ b/init/cras-directories.conf
@@ -0,0 +1 @@
+d /run/cras 1770 cras cras -
diff --git a/init/cras.conf b/init/cras.conf
new file mode 100644
index 00000000..eb1e62c7
--- /dev/null
+++ b/init/cras.conf
@@ -0,0 +1,25 @@
+# Copyright (c) 2012 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.
+
+# Installed by ADHD package.
+# cras upstart job.
+
+description "ChromeOS audio server"
+author "chromium-os-dev@chromium.org"
+
+env CRAS_SOCKET_DIR=/run/cras
+
+start on starting system-services
+stop on stopping system-services
+respawn
+
+# Allow the audio server real time priority.
+limit rtprio 12 12
+
+pre-start script
+ mkdir -p -m 1770 "${CRAS_SOCKET_DIR}"
+ chown -R cras:cras "${CRAS_SOCKET_DIR}"
+end script
+
+exec /bin/sh /usr/share/cros/init/cras.sh
diff --git a/init/cras.service b/init/cras.service
new file mode 100644
index 00000000..bda61c12
--- /dev/null
+++ b/init/cras.service
@@ -0,0 +1,14 @@
+# Copyright 2016 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.
+
+[Unit]
+Description=ChromeOS audio server
+PartOf=system-services.target
+After=system-services.target
+
+[Service]
+Restart=on-failure
+LimitRTPRIO=12
+TimeoutStopSec=20
+ExecStart=/bin/sh /usr/share/cros/init/cras.sh
diff --git a/init/cras.sh b/init/cras.sh
new file mode 100644
index 00000000..22842432
--- /dev/null
+++ b/init/cras.sh
@@ -0,0 +1,25 @@
+# Copyright 2016 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.
+
+# Disable HSP/HFP on Google WiFi (Gale) with UART-HCI Bluetooth
+# which is incapable to handle SCO audio.
+platform_name="$(mosys platform name)"
+if [ "$platform_name" = "Gale" ]; then
+ DISABLE_PROFILE="--disable_profile=hfp,hsp"
+fi
+# For board needs different device configs, check which config
+# directory to use. Use that directory for both volume curves
+# and dsp config.
+if [ -f /etc/cras/get_device_config_dir ]; then
+ device_config_dir="$(sh /etc/cras/get_device_config_dir)"
+ DEVICE_CONFIG_DIR="--device_config_dir=${device_config_dir}"
+ DSP_CONFIG="--dsp_config=${device_config_dir}/dsp.ini"
+fi
+if [ -f /etc/cras/get_internal_ucm_suffix ]; then
+ internal_ucm_suffix="$(sh /etc/cras/get_internal_ucm_suffix)"
+ INTERNAL_UCM_SUFFIX="--internal_ucm_suffix=${internal_ucm_suffix}"
+fi
+exec minijail0 -u cras -g cras -G -- /usr/bin/cras \
+ ${DSP_CONFIG} ${DEVICE_CONFIG_DIR} ${DISABLE_PROFILE} \
+ ${INTERNAL_UCM_SUFFIX}
diff --git a/scripts/audio_diagnostics b/scripts/audio_diagnostics
index 45f99b11..5ec96a9e 100755
--- a/scripts/audio_diagnostics
+++ b/scripts/audio_diagnostics
@@ -6,6 +6,16 @@
#
# Collect information about the audio system from top to bottom.
+dump_cards() {
+ for card in ${@}
+ do
+ echo '=== amixer -c' $card scontents '==='
+ amixer -c $card scontents
+ echo '=== amixer -c' $card contents '==='
+ amixer -c $card contents
+ done
+}
+
echo '=== cras_test_client --dump_server_info ==='
cras_test_client --dump_server_info
@@ -17,14 +27,11 @@ aplay -l
echo '=== arecord -l ==='
arecord -l
-cards=$(aplay -l | egrep ^card | sed 's/card \([0-9]\+\).*/\1/' | sort -u)
-for card in $cards
-do
- echo '=== amixer -c' $card scontents '==='
- amixer -c $card scontents
- echo '=== amixer -c' $card contents '==='
- amixer -c $card contents
-done
+output_cards=$(aplay -l | egrep ^card | sed 's/card \([0-9]\+\).*/\1/' | sort -u)
+dump_cards $output_cards
+
+input_cards=$(arecord -l | egrep ^card | sed 's/card \([0-9]\+\).*/\1/' | sort -u)
+dump_cards $input_cards
# HDA codec for codecs on x86.
codecs=$(find /proc/asound -mindepth 2 -maxdepth 2 -path '*card*/codec#*')
diff --git a/scripts/audio_thread_log_viewer/log.test b/scripts/audio_thread_log_viewer/log.test
new file mode 100644
index 00000000..8206761d
--- /dev/null
+++ b/scripts/audio_thread_log_viewer/log.test
@@ -0,0 +1,6151 @@
+Audio Debug Stats:
+-------------devices------------
+Output dev: bdw-rt5677: :1,0
+65536 0 1024 0 48000 2 1.000000
+-------------stream_dump------------
+Audio Thread Event Log:
+start at 736
+ 496098.524565708 DEV_SLEEP_TIME dev:8 wake:000496098.545892346
+ 496098.524567126 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.545959543 WAKE num_fds:0
+ 496098.545994178 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496098.546000808 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.546001355 DEV_SLEEP_TIME dev:8 wake:000496098.567328258
+ 496098.546002783 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.567581739 WAKE num_fds:0
+ 496098.567615773 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496098.567622518 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.567623059 DEV_SLEEP_TIME dev:8 wake:000496098.588949912
+ 496098.567624543 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.589207274 WAKE num_fds:0
+ 496098.589240896 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496098.589247582 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.589248133 DEV_SLEEP_TIME dev:8 wake:000496098.610574976
+ 496098.589250062 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.610821201 WAKE num_fds:0
+ 496098.610854808 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496098.610861519 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.610862050 DEV_SLEEP_TIME dev:8 wake:000496098.632188948
+ 496098.610863463 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.632443051 WAKE num_fds:0
+ 496098.632476082 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496098.632482753 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.632483289 DEV_SLEEP_TIME dev:8 wake:000496098.653810157
+ 496098.632484727 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.654059981 WAKE num_fds:0
+ 496098.654093774 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496098.654100469 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.654101015 DEV_SLEEP_TIME dev:8 wake:000496098.675427884
+ 496098.654102514 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.675676644 WAKE num_fds:0
+ 496098.675709505 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496098.675715664 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.675716210 DEV_SLEEP_TIME dev:8 wake:000496098.697043620
+ 496098.675717624 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.697292000 WAKE num_fds:0
+ 496098.697325252 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496098.697331912 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.697332439 DEV_SLEEP_TIME dev:8 wake:000496098.718659357
+ 496098.697333932 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.718759444 WAKE num_fds:0
+ 496098.718791864 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496098.718798584 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.718799116 DEV_SLEEP_TIME dev:8 wake:000496098.740125964
+ 496098.718800569 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.740374739 WAKE num_fds:0
+ 496098.740407625 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496098.740414446 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.740414982 DEV_SLEEP_TIME dev:8 wake:000496098.761741745
+ 496098.740416420 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.761990271 WAKE num_fds:0
+ 496098.762023593 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496098.762030293 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.762030839 DEV_SLEEP_TIME dev:8 wake:000496098.783357712
+ 496098.762032303 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.783607275 WAKE num_fds:0
+ 496098.783640005 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496098.783646686 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.783647222 DEV_SLEEP_TIME dev:8 wake:000496098.804974110
+ 496098.783648665 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.805225207 WAKE num_fds:0
+ 496098.805258890 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496098.805265655 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.805266202 DEV_SLEEP_TIME dev:8 wake:000496098.826592999
+ 496098.805267675 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.826841354 WAKE num_fds:0
+ 496098.826874200 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496098.826880900 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.826881567 DEV_SLEEP_TIME dev:8 wake:000496098.848208295
+ 496098.826883075 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.848306238 WAKE num_fds:0
+ 496098.848338928 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496098.848345624 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.848346165 DEV_SLEEP_TIME dev:8 wake:000496098.869673018
+ 496098.848367619 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.869943940 WAKE num_fds:0
+ 496098.869977317 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496098.869983927 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.869984473 DEV_SLEEP_TIME dev:8 wake:000496098.891311411
+ 496098.869985886 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.891556864 WAKE num_fds:0
+ 496098.891589740 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496098.891596290 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.891596837 DEV_SLEEP_TIME dev:8 wake:000496098.912923825
+ 496098.891598310 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.913173874 WAKE num_fds:0
+ 496098.913207412 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496098.913214077 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.913214734 DEV_SLEEP_TIME dev:8 wake:000496098.934541466
+ 496098.913216312 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.934788182 WAKE num_fds:0
+ 496098.934820552 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496098.934827267 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.934827799 DEV_SLEEP_TIME dev:8 wake:000496098.956154637
+ 496098.934829237 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.956406414 WAKE num_fds:0
+ 496098.956439565 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496098.956446341 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.956446872 DEV_SLEEP_TIME dev:8 wake:000496098.977773730
+ 496098.956448321 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.978020778 WAKE num_fds:0
+ 496098.978053749 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496098.978060374 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.978060916 DEV_SLEEP_TIME dev:8 wake:000496098.999387834
+ 496098.978062359 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496098.999634870 WAKE num_fds:0
+ 496098.999667952 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496098.999674592 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496098.999675198 DEV_SLEEP_TIME dev:8 wake:000496099.021002041
+ 496098.999676632 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.021101388 WAKE num_fds:0
+ 496099.021133918 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.021140623 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.021141155 DEV_SLEEP_TIME dev:8 wake:000496099.042468008
+ 496099.021142573 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.042718602 WAKE num_fds:0
+ 496099.042751734 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.042758414 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.042758961 DEV_SLEEP_TIME dev:8 wake:000496099.064085824
+ 496099.042760384 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.064330956 WAKE num_fds:0
+ 496099.064382264 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.064388955 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.064389501 DEV_SLEEP_TIME dev:8 wake:000496099.085716384
+ 496099.064390929 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.085964449 WAKE num_fds:0
+ 496099.085997670 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.086004286 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.086004822 DEV_SLEEP_TIME dev:8 wake:000496099.107331775
+ 496099.086006386 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.107586550 WAKE num_fds:0
+ 496099.107619801 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.107626397 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.107626938 DEV_SLEEP_TIME dev:8 wake:000496099.128953866
+ 496099.107628366 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.129204762 WAKE num_fds:0
+ 496099.129237403 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.129244023 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.129244569 DEV_SLEEP_TIME dev:8 wake:000496099.150571487
+ 496099.129246008 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.150641335 WAKE num_fds:0
+ 496099.150673374 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.150680004 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.150680656 DEV_SLEEP_TIME dev:8 wake:000496099.172007509
+ 496099.150682074 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.172256786 WAKE num_fds:0
+ 496099.172289878 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.172296493 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.172297034 DEV_SLEEP_TIME dev:8 wake:000496099.193623983
+ 496099.172298478 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.193874512 WAKE num_fds:0
+ 496099.193907859 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.193914469 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.193915006 DEV_SLEEP_TIME dev:8 wake:000496099.215241949
+ 496099.193916414 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.215492809 WAKE num_fds:0
+ 496099.215525304 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.215531864 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.215532421 DEV_SLEEP_TIME dev:8 wake:000496099.236859384
+ 496099.215533834 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.237111493 WAKE num_fds:0
+ 496099.237143973 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.237150678 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.237151210 DEV_SLEEP_TIME dev:8 wake:000496099.258478073
+ 496099.237152628 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.258726588 WAKE num_fds:0
+ 496099.258759554 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.258766124 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.258766665 DEV_SLEEP_TIME dev:8 wake:000496099.280093668
+ 496099.258768219 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.280340434 WAKE num_fds:0
+ 496099.280393006 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.280399631 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.280400182 DEV_SLEEP_TIME dev:8 wake:000496099.301727090
+ 496099.280401641 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.301975591 WAKE num_fds:0
+ 496099.302008723 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.302015493 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.302016025 DEV_SLEEP_TIME dev:8 wake:000496099.323342868
+ 496099.302017463 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.323440980 WAKE num_fds:0
+ 496099.323477615 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.323484245 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.323484791 DEV_SLEEP_TIME dev:8 wake:000496099.344811715
+ 496099.323486230 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.345086125 WAKE num_fds:0
+ 496099.345126263 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.345131465 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.345131996 DEV_SLEEP_TIME dev:8 wake:000496099.366460378
+ 496099.345133429 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.366708151 WAKE num_fds:0
+ 496099.366739964 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.366746760 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.366747432 DEV_SLEEP_TIME dev:8 wake:000496099.388074069
+ 496099.366748880 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.388144614 WAKE num_fds:0
+ 496099.388176969 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.388183740 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.388184281 DEV_SLEEP_TIME dev:8 wake:000496099.409511159
+ 496099.388185724 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.409545707 WAKE num_fds:0
+ 496099.409566242 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.409571665 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.409571919 DEV_SLEEP_TIME dev:8 wake:000496099.430899910
+ 496099.409572582 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.430927759 WAKE num_fds:0
+ 496099.430955957 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.430962098 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.430962539 DEV_SLEEP_TIME dev:8 wake:000496099.452289928
+ 496099.430963662 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.452379104 WAKE num_fds:0
+ 496099.452413514 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.452418641 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.452419177 DEV_SLEEP_TIME dev:8 wake:000496099.473747588
+ 496099.452420710 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.473884983 WAKE num_fds:0
+ 496099.473917934 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.473924589 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.473925130 DEV_SLEEP_TIME dev:8 wake:000496099.495252069
+ 496099.473926564 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.495367456 WAKE num_fds:0
+ 496099.495399996 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.495406612 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.495407158 DEV_SLEEP_TIME dev:8 wake:000496099.516734061
+ 496099.495408727 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.516763201 WAKE num_fds:0
+ 496099.516795615 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.516802181 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.516802732 DEV_SLEEP_TIME dev:8 wake:000496099.538129745
+ 496099.516804095 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.538207146 WAKE num_fds:0
+ 496099.538238564 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.538245094 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.538245665 DEV_SLEEP_TIME dev:8 wake:000496099.559572673
+ 496099.538247123 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.559628849 WAKE num_fds:0
+ 496099.559659896 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.559665359 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.559665890 DEV_SLEEP_TIME dev:8 wake:000496099.580994046
+ 496099.559667333 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.581019938 WAKE num_fds:0
+ 496099.581050679 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.581057460 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.581057996 DEV_SLEEP_TIME dev:8 wake:000496099.602384964
+ 496099.581059284 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.602418768 WAKE num_fds:0
+ 496099.602454421 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.602462449 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.602463206 DEV_SLEEP_TIME dev:8 wake:000496099.623788801
+ 496099.602465055 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.623858429 WAKE num_fds:0
+ 496099.623890809 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.623897589 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.623898131 DEV_SLEEP_TIME dev:8 wake:000496099.645224914
+ 496099.623899609 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.645296641 WAKE num_fds:0
+ 496099.645334724 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.645340272 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.645340808 DEV_SLEEP_TIME dev:8 wake:000496099.666668954
+ 496099.645342192 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.666692849 WAKE num_fds:0
+ 496099.666719109 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.666725256 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.666725704 DEV_SLEEP_TIME dev:8 wake:000496099.688053104
+ 496099.666726894 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.688074387 WAKE num_fds:0
+ 496099.688096225 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.688101841 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.688102175 DEV_SLEEP_TIME dev:8 wake:000496099.709430072
+ 496099.688102887 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.709443343 WAKE num_fds:0
+ 496099.709455328 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.709459005 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.709459229 DEV_SLEEP_TIME dev:8 wake:000496099.730788957
+ 496099.709459736 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.730805574 WAKE num_fds:0
+ 496099.730824543 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.730829830 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.730830098 DEV_SLEEP_TIME dev:8 wake:000496099.752158351
+ 496099.730830581 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.752172469 WAKE num_fds:0
+ 496099.752187265 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.752190979 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.752191240 DEV_SLEEP_TIME dev:8 wake:000496099.773520970
+ 496099.752191743 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.773533690 WAKE num_fds:0
+ 496099.773549159 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.773552607 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.773552834 DEV_SLEEP_TIME dev:8 wake:000496099.794882783
+ 496099.773553393 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.794893261 WAKE num_fds:0
+ 496099.794905112 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.794908388 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.794908531 DEV_SLEEP_TIME dev:8 wake:000496099.816238663
+ 496099.794908990 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.816248944 WAKE num_fds:0
+ 496099.816260791 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.816263965 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.816264138 DEV_SLEEP_TIME dev:8 wake:000496099.837594355
+ 496099.816264504 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.837604072 WAKE num_fds:0
+ 496099.837616853 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.837619986 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.837620168 DEV_SLEEP_TIME dev:8 wake:000496099.858950398
+ 496099.837620511 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.858961345 WAKE num_fds:0
+ 496099.858976724 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.858979930 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.858980107 DEV_SLEEP_TIME dev:8 wake:000496099.880310281
+ 496099.858980424 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.880319596 WAKE num_fds:0
+ 496099.880330501 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.880333657 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.880333835 DEV_SLEEP_TIME dev:8 wake:000496099.901664064
+ 496099.880334293 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.901673173 WAKE num_fds:0
+ 496099.901683309 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.901686399 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.901686518 DEV_SLEEP_TIME dev:8 wake:000496099.923016808
+ 496099.901686986 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.923026422 WAKE num_fds:0
+ 496099.923039650 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.923042794 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.923042971 DEV_SLEEP_TIME dev:8 wake:000496099.944373193
+ 496099.923043433 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.944382798 WAKE num_fds:0
+ 496099.944393969 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.944397102 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.944397285 DEV_SLEEP_TIME dev:8 wake:000496099.965727428
+ 496099.944397681 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.965737960 WAKE num_fds:0
+ 496099.965751311 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496099.965754524 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.965754702 DEV_SLEEP_TIME dev:8 wake:000496099.987084869
+ 496099.965755111 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496099.987093719 WAKE num_fds:0
+ 496099.987104561 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496099.987107677 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496099.987107905 DEV_SLEEP_TIME dev:8 wake:000496100.008438069
+ 496099.987108340 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.008446847 WAKE num_fds:0
+ 496100.008457382 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.008460682 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.008460869 DEV_SLEEP_TIME dev:8 wake:000496100.029790961
+ 496100.008461246 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.029818678 WAKE num_fds:0
+ 496100.029837149 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.029843078 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.029843305 DEV_SLEEP_TIME dev:8 wake:000496100.051171009
+ 496100.029843952 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.051190229 WAKE num_fds:0
+ 496100.051209390 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.051214799 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.051215052 DEV_SLEEP_TIME dev:8 wake:000496100.072543113
+ 496100.051215796 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.072561196 WAKE num_fds:0
+ 496100.072576819 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.072580750 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.072581115 DEV_SLEEP_TIME dev:8 wake:000496100.093910689
+ 496100.072581922 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.093973259 WAKE num_fds:0
+ 496100.093991762 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.093995366 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.093995594 DEV_SLEEP_TIME dev:8 wake:000496100.115325510
+ 496100.093996174 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.115537814 WAKE num_fds:0
+ 496100.115557401 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.115560841 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.115561080 DEV_SLEEP_TIME dev:8 wake:000496100.136891036
+ 496100.115561681 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.137102895 WAKE num_fds:0
+ 496100.137121895 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.137125352 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.137125589 DEV_SLEEP_TIME dev:8 wake:000496100.158455544
+ 496100.137126194 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.158690778 WAKE num_fds:0
+ 496100.158718867 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.158724914 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.158725362 DEV_SLEEP_TIME dev:8 wake:000496100.180052858
+ 496100.158726597 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.180291907 WAKE num_fds:0
+ 496100.180334939 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.180341137 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.180341694 DEV_SLEEP_TIME dev:8 wake:000496100.201668888
+ 496100.180342901 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.201904895 WAKE num_fds:0
+ 496100.201935065 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.201941215 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.201941653 DEV_SLEEP_TIME dev:8 wake:000496100.223269028
+ 496100.201942826 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.223392063 WAKE num_fds:0
+ 496100.223421828 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.223427926 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.223428484 DEV_SLEEP_TIME dev:8 wake:000496100.244755795
+ 496100.223429666 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.245001270 WAKE num_fds:0
+ 496100.245034126 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.245041097 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.245041638 DEV_SLEEP_TIME dev:8 wake:000496100.266368371
+ 496100.245043102 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.266620745 WAKE num_fds:0
+ 496100.266654027 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.266660717 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.266661253 DEV_SLEEP_TIME dev:8 wake:000496100.287988172
+ 496100.266662682 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.288238777 WAKE num_fds:0
+ 496100.288272199 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.288278840 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.288279496 DEV_SLEEP_TIME dev:8 wake:000496100.309606269
+ 496100.288280920 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.309680702 WAKE num_fds:0
+ 496100.309714560 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.309721221 SET_DEV_WAKE dev:8 hw_level:2000 sleep:976
+ 496100.309721767 DEV_SLEEP_TIME dev:8 wake:000496100.330048685
+ 496100.309723215 SLEEP sleep:000000000.020333333 longest_wake:000158140
+ 496100.330297374 WAKE num_fds:0
+ 496100.330330320 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.330336875 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.330337422 DEV_SLEEP_TIME dev:8 wake:000496100.351664410
+ 496100.330338870 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.351764312 WAKE num_fds:0
+ 496100.351796516 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.351803227 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.351803773 DEV_SLEEP_TIME dev:8 wake:000496100.373130636
+ 496100.351805196 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.373381346 WAKE num_fds:0
+ 496100.373414302 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.373421028 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.373421579 DEV_SLEEP_TIME dev:8 wake:000496100.394748397
+ 496100.373422997 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.394996512 WAKE num_fds:0
+ 496100.395030009 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.395036710 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.395037256 DEV_SLEEP_TIME dev:8 wake:000496100.416364124
+ 496100.395038699 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.416614232 WAKE num_fds:0
+ 496100.416646743 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.416653363 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.416653904 DEV_SLEEP_TIME dev:8 wake:000496100.437980857
+ 496100.416655448 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.438214409 WAKE num_fds:0
+ 496100.438247169 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.438253865 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.438254401 DEV_SLEEP_TIME dev:8 wake:000496100.459581389
+ 496100.438255839 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.459827454 WAKE num_fds:0
+ 496100.459861848 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.459868483 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.459869060 DEV_SLEEP_TIME dev:8 wake:000496100.481195973
+ 496100.459870483 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.481448141 WAKE num_fds:0
+ 496100.481481212 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.481487983 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.481488559 DEV_SLEEP_TIME dev:8 wake:000496100.502815503
+ 496100.481489988 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.503061022 WAKE num_fds:0
+ 496100.503093867 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.503100483 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.503101054 DEV_SLEEP_TIME dev:8 wake:000496100.524427977
+ 496100.503102497 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.524533081 WAKE num_fds:0
+ 496100.524566132 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.524572718 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.524573259 DEV_SLEEP_TIME dev:8 wake:000496100.545900277
+ 496100.524574737 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.546150402 WAKE num_fds:0
+ 496100.546183914 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.546190600 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.546191141 DEV_SLEEP_TIME dev:8 wake:000496100.567517989
+ 496100.546192584 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.567766865 WAKE num_fds:0
+ 496100.567800633 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.567807248 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.567807789 DEV_SLEEP_TIME dev:8 wake:000496100.589134742
+ 496100.567809222 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.589386409 WAKE num_fds:0
+ 496100.589419551 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.589426096 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.589426642 DEV_SLEEP_TIME dev:8 wake:000496100.610753641
+ 496100.589428091 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.611000528 WAKE num_fds:0
+ 496100.611033584 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.611040440 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.611040991 DEV_SLEEP_TIME dev:8 wake:000496100.632367749
+ 496100.611042425 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.632618399 WAKE num_fds:0
+ 496100.632651826 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.632658577 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.632659118 DEV_SLEEP_TIME dev:8 wake:000496100.653985931
+ 496100.632660526 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.654089236 WAKE num_fds:0
+ 496100.654122483 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.654129063 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.654129610 DEV_SLEEP_TIME dev:8 wake:000496100.675456613
+ 496100.654131033 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.675707809 WAKE num_fds:0
+ 496100.675739668 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.675746323 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.675746884 DEV_SLEEP_TIME dev:8 wake:000496100.697073767
+ 496100.675748318 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.697320964 WAKE num_fds:0
+ 496100.697371546 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.697378417 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.697378968 DEV_SLEEP_TIME dev:8 wake:000496100.718705691
+ 496100.697380422 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.718805258 WAKE num_fds:0
+ 496100.718838966 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.718845586 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.718846132 DEV_SLEEP_TIME dev:8 wake:000496100.740173041
+ 496100.718847591 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.740425615 WAKE num_fds:0
+ 496100.740458811 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.740465657 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.740466203 DEV_SLEEP_TIME dev:8 wake:000496100.761793051
+ 496100.740467632 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.761892959 WAKE num_fds:0
+ 496100.761925459 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.761932105 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.761932776 DEV_SLEEP_TIME dev:8 wake:000496100.783259524
+ 496100.761934220 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.783381637 WAKE num_fds:0
+ 496100.783414944 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.783421680 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.783422226 DEV_SLEEP_TIME dev:8 wake:000496100.804749039
+ 496100.783423649 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.804853272 WAKE num_fds:0
+ 496100.804885807 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.804892417 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.804892963 DEV_SLEEP_TIME dev:8 wake:000496100.826219907
+ 496100.804894402 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.826322110 WAKE num_fds:0
+ 496100.826373047 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.826380063 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.826380610 DEV_SLEEP_TIME dev:8 wake:000496100.847707192
+ 496100.826382018 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.847959501 WAKE num_fds:0
+ 496100.847992322 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.847999067 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.847999618 DEV_SLEEP_TIME dev:8 wake:000496100.869326431
+ 496100.848001317 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.869578930 WAKE num_fds:0
+ 496100.869612292 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.869619023 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.869619574 DEV_SLEEP_TIME dev:8 wake:000496100.890946412
+ 496100.869620997 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.891197770 WAKE num_fds:0
+ 496100.891230550 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.891237110 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.891237787 DEV_SLEEP_TIME dev:8 wake:000496100.912564655
+ 496100.891239220 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.912816197 WAKE num_fds:0
+ 496100.912849323 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.912855848 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.912856420 DEV_SLEEP_TIME dev:8 wake:000496100.934183418
+ 496100.912857878 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.934430826 WAKE num_fds:0
+ 496100.934464102 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.934470708 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.934471254 DEV_SLEEP_TIME dev:8 wake:000496100.955798272
+ 496100.934472697 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.955901337 WAKE num_fds:0
+ 496100.955933948 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.955940678 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.955941229 DEV_SLEEP_TIME dev:8 wake:000496100.977268047
+ 496100.955942683 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.977519955 WAKE num_fds:0
+ 496100.977552550 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496100.977559256 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.977559822 DEV_SLEEP_TIME dev:8 wake:000496100.998886650
+ 496100.977561271 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496100.999137892 WAKE num_fds:0
+ 496100.999169961 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496100.999176612 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496100.999177088 DEV_SLEEP_TIME dev:8 wake:000496101.020504061
+ 496100.999178456 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.020757502 WAKE num_fds:0
+ 496101.020790759 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.020797399 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.020797941 DEV_SLEEP_TIME dev:8 wake:000496101.042124944
+ 496101.020799364 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.042376561 WAKE num_fds:0
+ 496101.042409227 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.042415827 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.042416388 DEV_SLEEP_TIME dev:8 wake:000496101.063743316
+ 496101.042417987 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.063993676 WAKE num_fds:0
+ 496101.064026808 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.064033453 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.064033994 DEV_SLEEP_TIME dev:8 wake:000496101.085360928
+ 496101.064035438 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.085608450 WAKE num_fds:0
+ 496101.085642659 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.085649365 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.085649901 DEV_SLEEP_TIME dev:8 wake:000496101.106976764
+ 496101.085651324 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.107228552 WAKE num_fds:0
+ 496101.107261478 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.107268179 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.107268720 DEV_SLEEP_TIME dev:8 wake:000496101.128595704
+ 496101.107270164 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.128696698 WAKE num_fds:0
+ 496101.128726381 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.128733227 SET_DEV_WAKE dev:8 hw_level:2000 sleep:976
+ 496101.128733768 DEV_SLEEP_TIME dev:8 wake:000496101.149060501
+ 496101.128735212 SLEEP sleep:000000000.020333333 longest_wake:000158140
+ 496101.149310393 WAKE num_fds:0
+ 496101.149343359 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.149381326 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.149381917 DEV_SLEEP_TIME dev:8 wake:000496101.170677469
+ 496101.149383436 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.170961115 WAKE num_fds:0
+ 496101.170994692 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.171001378 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.171001924 DEV_SLEEP_TIME dev:8 wake:000496101.192328802
+ 496101.171003367 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.192580429 WAKE num_fds:0
+ 496101.192613290 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.192619910 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.192620462 DEV_SLEEP_TIME dev:8 wake:000496101.213947420
+ 496101.192621920 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.214192247 WAKE num_fds:0
+ 496101.214224998 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.214231573 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.214232119 DEV_SLEEP_TIME dev:8 wake:000496101.235559088
+ 496101.214233553 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.235808094 WAKE num_fds:0
+ 496101.235840980 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.235847595 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.235848146 DEV_SLEEP_TIME dev:8 wake:000496101.257175089
+ 496101.235849590 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.257275849 WAKE num_fds:0
+ 496101.257308500 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.257315135 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.257315676 DEV_SLEEP_TIME dev:8 wake:000496101.278642594
+ 496101.257317104 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.278887125 WAKE num_fds:0
+ 496101.278919525 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.278926135 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.278926676 DEV_SLEEP_TIME dev:8 wake:000496101.300253620
+ 496101.278928120 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.300391730 WAKE num_fds:0
+ 496101.300424490 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.300431176 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.300431722 DEV_SLEEP_TIME dev:8 wake:000496101.321758585
+ 496101.300433160 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.322005658 WAKE num_fds:0
+ 496101.322038759 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.322045274 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.322045820 DEV_SLEEP_TIME dev:8 wake:000496101.343372864
+ 496101.322047254 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.343623649 WAKE num_fds:0
+ 496101.343656334 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.343662930 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.343663471 DEV_SLEEP_TIME dev:8 wake:000496101.364990429
+ 496101.343664909 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.365239140 WAKE num_fds:0
+ 496101.365272096 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.365278837 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.365279378 DEV_SLEEP_TIME dev:8 wake:000496101.386606216
+ 496101.365280832 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.386856460 WAKE num_fds:0
+ 496101.386890138 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.386896783 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.386897330 DEV_SLEEP_TIME dev:8 wake:000496101.408224208
+ 496101.386898763 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.408472672 WAKE num_fds:0
+ 496101.408505724 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.408512364 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.408512905 DEV_SLEEP_TIME dev:8 wake:000496101.429839848
+ 496101.408514329 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.429906550 WAKE num_fds:0
+ 496101.429938428 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.429944993 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.429945535 DEV_SLEEP_TIME dev:8 wake:000496101.451272568
+ 496101.429946993 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.451524275 WAKE num_fds:0
+ 496101.451557176 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.451563777 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.451564328 DEV_SLEEP_TIME dev:8 wake:000496101.472891271
+ 496101.451565906 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.473137346 WAKE num_fds:0
+ 496101.473170368 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.473177068 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.473177609 DEV_SLEEP_TIME dev:8 wake:000496101.494504467
+ 496101.473179058 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.494599058 WAKE num_fds:0
+ 496101.494630274 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.494636524 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.494637017 DEV_SLEEP_TIME dev:8 wake:000496101.515964292
+ 496101.494638308 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.516182970 WAKE num_fds:0
+ 496101.516214183 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.516220585 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.516221074 DEV_SLEEP_TIME dev:8 wake:000496101.537548225
+ 496101.516222409 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.537791076 WAKE num_fds:0
+ 496101.537822243 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.537828687 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.537829164 DEV_SLEEP_TIME dev:8 wake:000496101.559156302
+ 496101.537830627 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.559252688 WAKE num_fds:0
+ 496101.559283407 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.559289745 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.559290243 DEV_SLEEP_TIME dev:8 wake:000496101.580617438
+ 496101.559291526 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.580861870 WAKE num_fds:0
+ 496101.580893182 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.580899553 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.580900034 DEV_SLEEP_TIME dev:8 wake:000496101.602227209
+ 496101.580901321 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.602471830 WAKE num_fds:0
+ 496101.602503226 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.602509601 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.602510086 DEV_SLEEP_TIME dev:8 wake:000496101.623837257
+ 496101.602511361 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.624078044 WAKE num_fds:0
+ 496101.624109220 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.624115607 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.624116088 DEV_SLEEP_TIME dev:8 wake:000496101.645443234
+ 496101.624117382 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.645685689 WAKE num_fds:0
+ 496101.645715144 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.645721455 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.645721940 DEV_SLEEP_TIME dev:8 wake:000496101.667049171
+ 496101.645723231 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.667144488 WAKE num_fds:0
+ 496101.667175468 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.667182259 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.667182761 DEV_SLEEP_TIME dev:8 wake:000496101.688509494
+ 496101.667184067 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.688754196 WAKE num_fds:0
+ 496101.688785797 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.688792043 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.688792544 DEV_SLEEP_TIME dev:8 wake:000496101.710119799
+ 496101.688793835 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.710368568 WAKE num_fds:0
+ 496101.710399467 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.710405854 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.710406339 DEV_SLEEP_TIME dev:8 wake:000496101.731733498
+ 496101.710407618 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.731827954 WAKE num_fds:0
+ 496101.731858565 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.731864803 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.731865301 DEV_SLEEP_TIME dev:8 wake:000496101.753192595
+ 496101.731866592 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.753434542 WAKE num_fds:0
+ 496101.753465201 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.753471516 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.753472073 DEV_SLEEP_TIME dev:8 wake:000496101.774799220
+ 496101.753473400 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.775034482 WAKE num_fds:0
+ 496101.775064488 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.775070890 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.775071391 DEV_SLEEP_TIME dev:8 wake:000496101.796398646
+ 496101.775072682 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.796638895 WAKE num_fds:0
+ 496101.796669879 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.796676257 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.796676734 DEV_SLEEP_TIME dev:8 wake:000496101.818003893
+ 496101.796678026 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.818247105 WAKE num_fds:0
+ 496101.818277917 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.818284203 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.818284684 DEV_SLEEP_TIME dev:8 wake:000496101.839611936
+ 496101.818285967 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.839856667 WAKE num_fds:0
+ 496101.839887703 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.839893985 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.839894482 DEV_SLEEP_TIME dev:8 wake:000496101.861221745
+ 496101.839895877 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.861461557 WAKE num_fds:0
+ 496101.861492472 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.861498774 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.861499264 DEV_SLEEP_TIME dev:8 wake:000496101.882826487
+ 496101.861500567 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.883067472 WAKE num_fds:0
+ 496101.883097802 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.883104085 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.883104586 DEV_SLEEP_TIME dev:8 wake:000496101.904431816
+ 496101.883105897 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.904675842 WAKE num_fds:0
+ 496101.904707142 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.904712154 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.904712656 DEV_SLEEP_TIME dev:8 wake:000496101.926041285
+ 496101.904713943 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.926286481 WAKE num_fds:0
+ 496101.926317412 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.926323746 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.926324244 DEV_SLEEP_TIME dev:8 wake:000496101.947651462
+ 496101.926325542 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.947894255 WAKE num_fds:0
+ 496101.947925656 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.947932034 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.947932528 DEV_SLEEP_TIME dev:8 wake:000496101.969259655
+ 496101.947933819 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.969503271 WAKE num_fds:0
+ 496101.969533801 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496101.969540176 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.969540650 DEV_SLEEP_TIME dev:8 wake:000496101.990867828
+ 496101.969541940 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496101.991112235 WAKE num_fds:0
+ 496101.991142850 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496101.991149285 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496101.991149702 DEV_SLEEP_TIME dev:8 wake:000496102.012476969
+ 496101.991151093 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.012720280 WAKE num_fds:0
+ 496102.012751156 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.012757551 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.012758032 DEV_SLEEP_TIME dev:8 wake:000496102.034085183
+ 496102.012759347 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.034179223 WAKE num_fds:0
+ 496102.034209573 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.034215959 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.034216445 DEV_SLEEP_TIME dev:8 wake:000496102.055543612
+ 496102.034217748 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.055786354 WAKE num_fds:0
+ 496102.055817274 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.055823596 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.055824098 DEV_SLEEP_TIME dev:8 wake:000496102.077151308
+ 496102.055825385 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.077396578 WAKE num_fds:0
+ 496102.077427578 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.077433828 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.077434386 DEV_SLEEP_TIME dev:8 wake:000496102.098761576
+ 496102.077435665 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.099004172 WAKE num_fds:0
+ 496102.099035175 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.099041530 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.099042024 DEV_SLEEP_TIME dev:8 wake:000496102.120369198
+ 496102.099043326 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.120613023 WAKE num_fds:0
+ 496102.120644090 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.120650614 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.120651091 DEV_SLEEP_TIME dev:8 wake:000496102.141978225
+ 496102.120652494 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.142225706 WAKE num_fds:0
+ 496102.142256890 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.142263153 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.142263645 DEV_SLEEP_TIME dev:8 wake:000496102.163590920
+ 496102.142265037 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.163836347 WAKE num_fds:0
+ 496102.163867587 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.163873914 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.163874460 DEV_SLEEP_TIME dev:8 wake:000496102.185201638
+ 496102.163875798 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.185447500 WAKE num_fds:0
+ 496102.185478163 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.185484509 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.185484991 DEV_SLEEP_TIME dev:8 wake:000496102.206812162
+ 496102.185486293 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.207058359 WAKE num_fds:0
+ 496102.207089563 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.207095821 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.207096391 DEV_SLEEP_TIME dev:8 wake:000496102.228423577
+ 496102.207097693 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.228668781 WAKE num_fds:0
+ 496102.228701857 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.228707109 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.228707636 DEV_SLEEP_TIME dev:8 wake:000496102.250035927
+ 496102.228709310 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.250285696 WAKE num_fds:0
+ 496102.250317905 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.250324535 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.250325072 DEV_SLEEP_TIME dev:8 wake:000496102.271651990
+ 496102.250326550 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.271905241 WAKE num_fds:0
+ 496102.271938628 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.271945223 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.271945764 DEV_SLEEP_TIME dev:8 wake:000496102.293272728
+ 496102.271947358 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.293521749 WAKE num_fds:0
+ 496102.293554584 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.293561180 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.293561721 DEV_SLEEP_TIME dev:8 wake:000496102.314888689
+ 496102.293563174 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.315141390 WAKE num_fds:0
+ 496102.315173950 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.315180640 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.315181177 DEV_SLEEP_TIME dev:8 wake:000496102.336508085
+ 496102.315182615 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.336608964 WAKE num_fds:0
+ 496102.336641554 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.336648134 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.336648786 DEV_SLEEP_TIME dev:8 wake:000496102.357975634
+ 496102.336650354 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.358060502 WAKE num_fds:0
+ 496102.358092746 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.358099572 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.358100118 DEV_SLEEP_TIME dev:8 wake:000496102.379426886
+ 496102.358101582 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.379679310 WAKE num_fds:0
+ 496102.379711860 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.379717002 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.379717538 DEV_SLEEP_TIME dev:8 wake:000496102.401045965
+ 496102.379718967 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.401289935 WAKE num_fds:0
+ 496102.401322290 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.401328976 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.401329512 DEV_SLEEP_TIME dev:8 wake:000496102.422656405
+ 496102.401330940 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.422906689 WAKE num_fds:0
+ 496102.422940056 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.422946696 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.422947232 DEV_SLEEP_TIME dev:8 wake:000496102.444274171
+ 496102.422948681 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.444528128 WAKE num_fds:0
+ 496102.444561089 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.444567699 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.444568241 DEV_SLEEP_TIME dev:8 wake:000496102.465895209
+ 496102.444569724 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.466143570 WAKE num_fds:0
+ 496102.466176561 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.466183417 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.466183943 DEV_SLEEP_TIME dev:8 wake:000496102.487510801
+ 496102.466185391 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.487759281 WAKE num_fds:0
+ 496102.487792688 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.487799288 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.487799839 DEV_SLEEP_TIME dev:8 wake:000496102.509126787
+ 496102.487801283 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.509382158 WAKE num_fds:0
+ 496102.509414728 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.509421519 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.509422050 DEV_SLEEP_TIME dev:8 wake:000496102.530748953
+ 496102.509423484 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.530997274 WAKE num_fds:0
+ 496102.531029418 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.531036073 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.531036610 DEV_SLEEP_TIME dev:8 wake:000496102.552363538
+ 496102.531038068 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.552613621 WAKE num_fds:0
+ 496102.552646277 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.552652837 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.552653398 DEV_SLEEP_TIME dev:8 wake:000496102.573980382
+ 496102.552654847 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.574235683 WAKE num_fds:0
+ 496102.574268759 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.574275550 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.574276086 DEV_SLEEP_TIME dev:8 wake:000496102.595602849
+ 496102.574277530 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.595703738 WAKE num_fds:0
+ 496102.595737742 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.595744357 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.595744903 DEV_SLEEP_TIME dev:8 wake:000496102.617071842
+ 496102.595746362 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.617323143 WAKE num_fds:0
+ 496102.617380144 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.617386815 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.617387476 DEV_SLEEP_TIME dev:8 wake:000496102.638714259
+ 496102.617388925 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.638973490 WAKE num_fds:0
+ 496102.639006691 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.639013432 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.639013983 DEV_SLEEP_TIME dev:8 wake:000496102.660340786
+ 496102.639015447 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.660417244 WAKE num_fds:0
+ 496102.660456845 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.660465896 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.660466528 DEV_SLEEP_TIME dev:8 wake:000496102.681791421
+ 496102.660468447 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.682044914 WAKE num_fds:0
+ 496102.682077384 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.682084109 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.682084761 DEV_SLEEP_TIME dev:8 wake:000496102.703411513
+ 496102.682086214 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.703658395 WAKE num_fds:0
+ 496102.703692107 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.703698723 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.703699274 DEV_SLEEP_TIME dev:8 wake:000496102.725026202
+ 496102.703700717 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.725275906 WAKE num_fds:0
+ 496102.725309578 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.725316209 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.725316870 DEV_SLEEP_TIME dev:8 wake:000496102.746643673
+ 496102.725318439 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.746892644 WAKE num_fds:0
+ 496102.746925380 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.746932065 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.746932596 DEV_SLEEP_TIME dev:8 wake:000496102.768259499
+ 496102.746934020 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.768510786 WAKE num_fds:0
+ 496102.768543812 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.768550613 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.768551154 DEV_SLEEP_TIME dev:8 wake:000496102.789877957
+ 496102.768552672 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.790129755 WAKE num_fds:0
+ 496102.790162812 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.790169432 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.790169988 DEV_SLEEP_TIME dev:8 wake:000496102.811496916
+ 496102.790171416 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.811745406 WAKE num_fds:0
+ 496102.811779034 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.811785589 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.811786140 DEV_SLEEP_TIME dev:8 wake:000496102.833113134
+ 496102.811787564 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.833368700 WAKE num_fds:0
+ 496102.833408141 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.833416480 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.833417112 DEV_SLEEP_TIME dev:8 wake:000496102.854742566
+ 496102.833418956 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.854847040 WAKE num_fds:0
+ 496102.854879640 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.854886280 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.854886832 DEV_SLEEP_TIME dev:8 wake:000496102.876213770
+ 496102.854888265 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.876314619 WAKE num_fds:0
+ 496102.876375595 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.876382566 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.876383112 DEV_SLEEP_TIME dev:8 wake:000496102.897709815
+ 496102.876384550 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.897811827 WAKE num_fds:0
+ 496102.897845044 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.897851795 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.897852431 DEV_SLEEP_TIME dev:8 wake:000496102.919179159
+ 496102.897853869 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.919280565 WAKE num_fds:0
+ 496102.919313952 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.919320728 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.919321274 DEV_SLEEP_TIME dev:8 wake:000496102.940648037
+ 496102.919322707 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.940901293 WAKE num_fds:0
+ 496102.940934504 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.940941105 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.940941661 DEV_SLEEP_TIME dev:8 wake:000496102.962268614
+ 496102.940943109 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.962374715 WAKE num_fds:0
+ 496102.962408604 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496102.962415304 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.962415840 DEV_SLEEP_TIME dev:8 wake:000496102.983742834
+ 496102.962417289 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496102.983989801 WAKE num_fds:0
+ 496102.984022416 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496102.984029127 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496102.984029598 DEV_SLEEP_TIME dev:8 wake:000496103.005356531
+ 496102.984031001 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.005604715 WAKE num_fds:0
+ 496103.005637877 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.005644412 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.005644948 DEV_SLEEP_TIME dev:8 wake:000496103.026971961
+ 496103.005646371 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.027219901 WAKE num_fds:0
+ 496103.027252371 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.027259051 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.027259603 DEV_SLEEP_TIME dev:8 wake:000496103.048586471
+ 496103.027261016 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.048836534 WAKE num_fds:0
+ 496103.048869601 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.048876291 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.048876837 DEV_SLEEP_TIME dev:8 wake:000496103.070203690
+ 496103.048878406 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.070456866 WAKE num_fds:0
+ 496103.070490058 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.070496793 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.070497324 DEV_SLEEP_TIME dev:8 wake:000496103.091824152
+ 496103.070498778 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.092072047 WAKE num_fds:0
+ 496103.092104782 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.092111468 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.092112014 DEV_SLEEP_TIME dev:8 wake:000496103.113438897
+ 496103.092113457 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.113689061 WAKE num_fds:0
+ 496103.113722453 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.113729133 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.113729675 DEV_SLEEP_TIME dev:8 wake:000496103.135056578
+ 496103.113731128 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.135140323 WAKE num_fds:0
+ 496103.135172087 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.135178677 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.135179223 DEV_SLEEP_TIME dev:8 wake:000496103.156506176
+ 496103.135180651 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.156755693 WAKE num_fds:0
+ 496103.156789141 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.156795941 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.156796473 DEV_SLEEP_TIME dev:8 wake:000496103.178123281
+ 496103.156797911 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.178369600 WAKE num_fds:0
+ 496103.178402637 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.178409347 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.178409878 DEV_SLEEP_TIME dev:8 wake:000496103.199736726
+ 496103.178411312 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.199985012 WAKE num_fds:0
+ 496103.200018454 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.200025154 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.200025706 DEV_SLEEP_TIME dev:8 wake:000496103.221352569
+ 496103.200027164 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.221411887 WAKE num_fds:0
+ 496103.221449599 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.221457367 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.221458149 DEV_SLEEP_TIME dev:8 wake:000496103.242784100
+ 496103.221460163 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.243030786 WAKE num_fds:0
+ 496103.243063321 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.243069992 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.243070543 DEV_SLEEP_TIME dev:8 wake:000496103.264397441
+ 496103.243071991 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.264449458 WAKE num_fds:0
+ 496103.264482559 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.264489289 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.264489826 DEV_SLEEP_TIME dev:8 wake:000496103.285816679
+ 496103.264491259 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.285917784 WAKE num_fds:0
+ 496103.285950585 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.285957336 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.285957867 DEV_SLEEP_TIME dev:8 wake:000496103.307284700
+ 496103.285959300 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.307384411 WAKE num_fds:0
+ 496103.307417092 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.307423752 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.307424298 DEV_SLEEP_TIME dev:8 wake:000496103.328751181
+ 496103.307425727 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.328852327 WAKE num_fds:0
+ 496103.328885203 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.328891733 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.328892279 DEV_SLEEP_TIME dev:8 wake:000496103.350219293
+ 496103.328893698 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.350469121 WAKE num_fds:0
+ 496103.350501911 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.350508482 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.350509028 DEV_SLEEP_TIME dev:8 wake:000496103.371836026
+ 496103.350510602 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.372086512 WAKE num_fds:0
+ 496103.372119994 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.372126719 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.372127266 DEV_SLEEP_TIME dev:8 wake:000496103.393454084
+ 496103.372128694 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.393705741 WAKE num_fds:0
+ 496103.393738817 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.393745367 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.393745909 DEV_SLEEP_TIME dev:8 wake:000496103.415072907
+ 496103.393747357 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.415333750 WAKE num_fds:0
+ 496103.415396976 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.415404193 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.415404729 DEV_SLEEP_TIME dev:8 wake:000496103.436731547
+ 496103.415406162 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.436814764 WAKE num_fds:0
+ 496103.436838871 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.436844444 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.436844804 DEV_SLEEP_TIME dev:8 wake:000496103.458172708
+ 496103.436845753 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.458400798 WAKE num_fds:0
+ 496103.458425703 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.458431340 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.458431693 DEV_SLEEP_TIME dev:8 wake:000496103.479759544
+ 496103.458432626 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.479984935 WAKE num_fds:0
+ 496103.480009455 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.480014980 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.480015343 DEV_SLEEP_TIME dev:8 wake:000496103.501343283
+ 496103.480016274 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.501571552 WAKE num_fds:0
+ 496103.501596146 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.501601748 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.501602114 DEV_SLEEP_TIME dev:8 wake:000496103.522930023
+ 496103.501603047 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.523156936 WAKE num_fds:0
+ 496103.523181532 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.523187086 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.523187437 DEV_SLEEP_TIME dev:8 wake:000496103.544515344
+ 496103.523188369 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.544740125 WAKE num_fds:0
+ 496103.544764472 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.544770082 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.544770446 DEV_SLEEP_TIME dev:8 wake:000496103.566098335
+ 496103.544771384 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.566324453 WAKE num_fds:0
+ 496103.566371205 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.566377992 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.566378422 DEV_SLEEP_TIME dev:8 wake:000496103.587705271
+ 496103.566379814 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.587786308 WAKE num_fds:0
+ 496103.587811809 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.587817500 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.587817863 DEV_SLEEP_TIME dev:8 wake:000496103.609145680
+ 496103.587818973 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.609376370 WAKE num_fds:0
+ 496103.609400933 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.609406578 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.609406944 DEV_SLEEP_TIME dev:8 wake:000496103.630734830
+ 496103.609407885 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.630960573 WAKE num_fds:0
+ 496103.630985847 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.630991468 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.630991821 DEV_SLEEP_TIME dev:8 wake:000496103.652319678
+ 496103.630992762 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.652564901 WAKE num_fds:0
+ 496103.652597787 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.652604413 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.652604944 DEV_SLEEP_TIME dev:8 wake:000496103.673931877
+ 496103.652606367 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.674031369 WAKE num_fds:0
+ 496103.674064982 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.674071517 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.674072063 DEV_SLEEP_TIME dev:8 wake:000496103.695399066
+ 496103.674073511 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.695648238 WAKE num_fds:0
+ 496103.695681735 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.695688531 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.695689082 DEV_SLEEP_TIME dev:8 wake:000496103.717015820
+ 496103.695690631 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.717266806 WAKE num_fds:0
+ 496103.717300549 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.717307220 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.717307761 DEV_SLEEP_TIME dev:8 wake:000496103.738634664
+ 496103.717309194 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.738734857 WAKE num_fds:0
+ 496103.738766890 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.738773646 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.738774192 DEV_SLEEP_TIME dev:8 wake:000496103.760101015
+ 496103.738775641 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.760377525 WAKE num_fds:0
+ 496103.760411473 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.760418114 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.760418660 DEV_SLEEP_TIME dev:8 wake:000496103.781745613
+ 496103.760420098 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.781991819 WAKE num_fds:0
+ 496103.782024419 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.782031049 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.782031595 DEV_SLEEP_TIME dev:8 wake:000496103.803358509
+ 496103.782033034 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.803607936 WAKE num_fds:0
+ 496103.803640907 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.803647532 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.803648068 DEV_SLEEP_TIME dev:8 wake:000496103.824975016
+ 496103.803649532 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.825225166 WAKE num_fds:0
+ 496103.825257711 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.825264397 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.825264928 DEV_SLEEP_TIME dev:8 wake:000496103.846591806
+ 496103.825266386 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.846843268 WAKE num_fds:0
+ 496103.846875868 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.846882468 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.846883004 DEV_SLEEP_TIME dev:8 wake:000496103.868210043
+ 496103.846884453 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.868311589 WAKE num_fds:0
+ 496103.868369121 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.868377471 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.868378172 DEV_SLEEP_TIME dev:8 wake:000496103.889703602
+ 496103.868379906 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.889953992 WAKE num_fds:0
+ 496103.889987179 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.889993729 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.889994275 DEV_SLEEP_TIME dev:8 wake:000496103.911321284
+ 496103.889995693 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.911575091 WAKE num_fds:0
+ 496103.911608678 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.911615268 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.911615805 DEV_SLEEP_TIME dev:8 wake:000496103.932942778
+ 496103.911617248 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.933189961 WAKE num_fds:0
+ 496103.933222922 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.933229587 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.933230138 DEV_SLEEP_TIME dev:8 wake:000496103.954557037
+ 496103.933231577 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.954805246 WAKE num_fds:0
+ 496103.954837916 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.954844572 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.954845108 DEV_SLEEP_TIME dev:8 wake:000496103.976172026
+ 496103.954846546 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.976423272 WAKE num_fds:0
+ 496103.976456529 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496103.976463194 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.976463851 DEV_SLEEP_TIME dev:8 wake:000496103.997790639
+ 496103.976465369 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496103.998041575 WAKE num_fds:0
+ 496103.998074456 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496103.998081157 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496103.998081628 DEV_SLEEP_TIME dev:8 wake:000496104.019408596
+ 496103.998083011 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.019658098 WAKE num_fds:0
+ 496104.019691300 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.019697830 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.019698386 DEV_SLEEP_TIME dev:8 wake:000496104.041025390
+ 496104.019699855 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.041127397 WAKE num_fds:0
+ 496104.041159612 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.041166252 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.041166798 DEV_SLEEP_TIME dev:8 wake:000496104.062493731
+ 496104.041168216 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.062742647 WAKE num_fds:0
+ 496104.062776280 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.062782875 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.062783416 DEV_SLEEP_TIME dev:8 wake:000496104.084110370
+ 496104.062784850 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.084368467 WAKE num_fds:0
+ 496104.084401583 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.084408108 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.084408649 DEV_SLEEP_TIME dev:8 wake:000496104.105735673
+ 496104.084410068 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.105982034 WAKE num_fds:0
+ 496104.106015741 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.106022367 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.106022913 DEV_SLEEP_TIME dev:8 wake:000496104.127349861
+ 496104.106024341 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.127600586 WAKE num_fds:0
+ 496104.127633903 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.127640669 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.127641320 DEV_SLEEP_TIME dev:8 wake:000496104.148968038
+ 496104.127642744 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.149220703 WAKE num_fds:0
+ 496104.149253800 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.149260330 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.149260871 DEV_SLEEP_TIME dev:8 wake:000496104.170587935
+ 496104.149262269 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.170721179 WAKE num_fds:0
+ 496104.170754495 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.170761211 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.170761747 DEV_SLEEP_TIME dev:8 wake:000496104.192088610
+ 496104.170763170 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.192334148 WAKE num_fds:0
+ 496104.192385321 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.192391932 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.192392483 DEV_SLEEP_TIME dev:8 wake:000496104.213719466
+ 496104.192393901 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.213969626 WAKE num_fds:0
+ 496104.214003173 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.214009784 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.214010325 DEV_SLEEP_TIME dev:8 wake:000496104.235337263
+ 496104.214011758 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.235589010 WAKE num_fds:0
+ 496104.235622558 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.235629233 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.235629765 DEV_SLEEP_TIME dev:8 wake:000496104.256956668
+ 496104.235631193 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.257204853 WAKE num_fds:0
+ 496104.257238110 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.257244850 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.257245391 DEV_SLEEP_TIME dev:8 wake:000496104.278572214
+ 496104.257246820 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.278826247 WAKE num_fds:0
+ 496104.278861418 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.278868550 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.278869111 DEV_SLEEP_TIME dev:8 wake:000496104.300195693
+ 496104.278870524 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.300444238 WAKE num_fds:0
+ 496104.300477420 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.300484135 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.300484672 DEV_SLEEP_TIME dev:8 wake:000496104.321811585
+ 496104.300486110 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.322062401 WAKE num_fds:0
+ 496104.322095693 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.322102308 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.322102975 DEV_SLEEP_TIME dev:8 wake:000496104.343429772
+ 496104.322104433 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.343531524 WAKE num_fds:0
+ 496104.343564685 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.343571270 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.343571817 DEV_SLEEP_TIME dev:8 wake:000496104.364898785
+ 496104.343573265 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.365146048 WAKE num_fds:0
+ 496104.365179570 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.365186266 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.365186807 DEV_SLEEP_TIME dev:8 wake:000496104.386513665
+ 496104.365188245 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.386763683 WAKE num_fds:0
+ 496104.386797366 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.386803886 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.386804427 DEV_SLEEP_TIME dev:8 wake:000496104.408131476
+ 496104.386805876 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.408381509 WAKE num_fds:0
+ 496104.408415418 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.408422178 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.408422729 DEV_SLEEP_TIME dev:8 wake:000496104.429749482
+ 496104.408424293 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.430002674 WAKE num_fds:0
+ 496104.430036221 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.430042706 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.430043283 DEV_SLEEP_TIME dev:8 wake:000496104.451370316
+ 496104.430044801 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.451618891 WAKE num_fds:0
+ 496104.451651647 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.451658357 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.451658904 DEV_SLEEP_TIME dev:8 wake:000496104.472985797
+ 496104.451660332 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.473234513 WAKE num_fds:0
+ 496104.473268396 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.473274946 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.473275518 DEV_SLEEP_TIME dev:8 wake:000496104.494602496
+ 496104.473276971 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.494850299 WAKE num_fds:0
+ 496104.494883872 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.494890522 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.494891058 DEV_SLEEP_TIME dev:8 wake:000496104.516217982
+ 496104.494892497 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.516465429 WAKE num_fds:0
+ 496104.516497849 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.516504484 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.516505025 DEV_SLEEP_TIME dev:8 wake:000496104.537831949
+ 496104.516506474 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.538084509 WAKE num_fds:0
+ 496104.538116688 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.538123419 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.538123950 DEV_SLEEP_TIME dev:8 wake:000496104.559450813
+ 496104.538125398 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.559701493 WAKE num_fds:0
+ 496104.559734554 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.559741089 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.559741635 DEV_SLEEP_TIME dev:8 wake:000496104.581068669
+ 496104.559743104 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.581317008 WAKE num_fds:0
+ 496104.581373920 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.581381016 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.581381557 DEV_SLEEP_TIME dev:8 wake:000496104.602708135
+ 496104.581383061 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.602955819 WAKE num_fds:0
+ 496104.602989075 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.602995756 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.602996417 DEV_SLEEP_TIME dev:8 wake:000496104.624323190
+ 496104.602997951 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.624574071 WAKE num_fds:0
+ 496104.624606786 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.624613336 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.624613887 DEV_SLEEP_TIME dev:8 wake:000496104.645940886
+ 496104.624615321 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.646039125 WAKE num_fds:0
+ 496104.646072131 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.646078791 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.646079338 DEV_SLEEP_TIME dev:8 wake:000496104.667406256
+ 496104.646080766 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.667623494 WAKE num_fds:0
+ 496104.667664082 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.667672797 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.667673469 DEV_SLEEP_TIME dev:8 wake:000496104.688999335
+ 496104.667675403 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.689252361 WAKE num_fds:0
+ 496104.689285583 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.689292348 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.689292895 DEV_SLEEP_TIME dev:8 wake:000496104.710619672
+ 496104.689294348 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.710870232 WAKE num_fds:0
+ 496104.710903940 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.710910650 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.710911312 DEV_SLEEP_TIME dev:8 wake:000496104.732238050
+ 496104.710912735 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.732487847 WAKE num_fds:0
+ 496104.732520778 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.732527314 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.732527860 DEV_SLEEP_TIME dev:8 wake:000496104.753854878
+ 496104.732529308 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.754105003 WAKE num_fds:0
+ 496104.754137408 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.754144128 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.754144664 DEV_SLEEP_TIME dev:8 wake:000496104.775471517
+ 496104.754146103 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.775719892 WAKE num_fds:0
+ 496104.775752763 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.775759303 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.775759849 DEV_SLEEP_TIME dev:8 wake:000496104.797086893
+ 496104.775761277 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.797331022 WAKE num_fds:0
+ 496104.797383769 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.797390394 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.797390941 DEV_SLEEP_TIME dev:8 wake:000496104.818717909
+ 496104.797392374 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.818964300 WAKE num_fds:0
+ 496104.818996575 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.819003275 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.819003816 DEV_SLEEP_TIME dev:8 wake:000496104.840330689
+ 496104.819005234 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.840580843 WAKE num_fds:0
+ 496104.840613864 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.840620555 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.840621086 DEV_SLEEP_TIME dev:8 wake:000496104.861947939
+ 496104.840622509 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.862198068 WAKE num_fds:0
+ 496104.862231100 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.862237675 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.862238216 DEV_SLEEP_TIME dev:8 wake:000496104.883565189
+ 496104.862239805 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.883815458 WAKE num_fds:0
+ 496104.883848640 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.883855210 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.883855756 DEV_SLEEP_TIME dev:8 wake:000496104.905182735
+ 496104.883857185 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.905430593 WAKE num_fds:0
+ 496104.905463559 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.905470210 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.905470756 DEV_SLEEP_TIME dev:8 wake:000496104.926797684
+ 496104.905472199 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.927045448 WAKE num_fds:0
+ 496104.927078660 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.927085360 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.927085897 DEV_SLEEP_TIME dev:8 wake:000496104.948412810
+ 496104.927087325 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.948489252 WAKE num_fds:0
+ 496104.948522459 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.948529140 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.948529691 DEV_SLEEP_TIME dev:8 wake:000496104.969856534
+ 496104.948531134 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.969958296 WAKE num_fds:0
+ 496104.969991277 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496104.969997942 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.969998494 DEV_SLEEP_TIME dev:8 wake:000496104.991325397
+ 496104.969999942 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496104.991576864 WAKE num_fds:0
+ 496104.991610752 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496104.991617362 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496104.991617838 DEV_SLEEP_TIME dev:8 wake:000496105.012944852
+ 496104.991619206 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.013197317 WAKE num_fds:0
+ 496105.013229992 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.013236557 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.013237093 DEV_SLEEP_TIME dev:8 wake:000496105.034564082
+ 496105.013238527 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.034815719 WAKE num_fds:0
+ 496105.034849256 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.034855866 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.034856413 DEV_SLEEP_TIME dev:8 wake:000496105.056183386
+ 496105.034857836 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.056432743 WAKE num_fds:0
+ 496105.056465699 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.056472284 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.056472830 DEV_SLEEP_TIME dev:8 wake:000496105.077799809
+ 496105.056474259 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.078050495 WAKE num_fds:0
+ 496105.078083877 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.078090507 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.078091058 DEV_SLEEP_TIME dev:8 wake:000496105.099417966
+ 496105.078092492 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.099669604 WAKE num_fds:0
+ 496105.099702540 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.099707727 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.099708263 DEV_SLEEP_TIME dev:8 wake:000496105.121036634
+ 496105.099709746 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.121285461 WAKE num_fds:0
+ 496105.121317951 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.121324611 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.121325268 DEV_SLEEP_TIME dev:8 wake:000496105.142652076
+ 496105.121326736 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.142903778 WAKE num_fds:0
+ 496105.142936749 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.142943460 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.142943991 DEV_SLEEP_TIME dev:8 wake:000496105.164270849
+ 496105.142945419 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.164519885 WAKE num_fds:0
+ 496105.164552300 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.164558900 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.164559441 DEV_SLEEP_TIME dev:8 wake:000496105.185886405
+ 496105.164560985 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.186137030 WAKE num_fds:0
+ 496105.186170513 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.186177243 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.186177769 DEV_SLEEP_TIME dev:8 wake:000496105.207504597
+ 496105.186179198 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.207754355 WAKE num_fds:0
+ 496105.207786910 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.207793495 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.207794042 DEV_SLEEP_TIME dev:8 wake:000496105.229121015
+ 496105.207795495 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.229371835 WAKE num_fds:0
+ 496105.229404380 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.229411061 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.229411587 DEV_SLEEP_TIME dev:8 wake:000496105.250738520
+ 496105.229413065 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.250989191 WAKE num_fds:0
+ 496105.251022533 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.251029098 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.251029649 DEV_SLEEP_TIME dev:8 wake:000496105.272356643
+ 496105.251031078 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.272465525 WAKE num_fds:0
+ 496105.272498271 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.272505137 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.272505678 DEV_SLEEP_TIME dev:8 wake:000496105.293832411
+ 496105.272507131 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.294080130 WAKE num_fds:0
+ 496105.294113276 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.294119922 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.294120458 DEV_SLEEP_TIME dev:8 wake:000496105.315447346
+ 496105.294121896 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.315695525 WAKE num_fds:0
+ 496105.315727564 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.315734204 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.315734751 DEV_SLEEP_TIME dev:8 wake:000496105.337061674
+ 496105.315736194 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.337311722 WAKE num_fds:0
+ 496105.337363963 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.337370999 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.337371535 DEV_SLEEP_TIME dev:8 wake:000496105.358698148
+ 496105.337373004 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.358950763 WAKE num_fds:0
+ 496105.358983855 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.358990410 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.358990956 DEV_SLEEP_TIME dev:8 wake:000496105.380317989
+ 496105.358992429 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.380568549 WAKE num_fds:0
+ 496105.380601365 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.380607900 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.380608451 DEV_SLEEP_TIME dev:8 wake:000496105.401935460
+ 496105.380609894 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.402180688 WAKE num_fds:0
+ 496105.402212987 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.402219633 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.402220169 DEV_SLEEP_TIME dev:8 wake:000496105.423547082
+ 496105.402221627 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.423793407 WAKE num_fds:0
+ 496105.423826664 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.423833274 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.423833815 DEV_SLEEP_TIME dev:8 wake:000496105.445160758
+ 496105.423835239 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.445409013 WAKE num_fds:0
+ 496105.445441924 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.445448494 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.445449045 DEV_SLEEP_TIME dev:8 wake:000496105.466776074
+ 496105.445450639 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.467024434 WAKE num_fds:0
+ 496105.467057591 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.467064336 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.467064882 DEV_SLEEP_TIME dev:8 wake:000496105.488391700
+ 496105.467066331 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.488641904 WAKE num_fds:0
+ 496105.488674479 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.488681345 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.488681882 DEV_SLEEP_TIME dev:8 wake:000496105.510008579
+ 496105.488683310 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.510259461 WAKE num_fds:0
+ 496105.510292828 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.510299403 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.510299944 DEV_SLEEP_TIME dev:8 wake:000496105.531626892
+ 496105.510301372 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.531877692 WAKE num_fds:0
+ 496105.531910704 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.531917324 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.531917855 DEV_SLEEP_TIME dev:8 wake:000496105.553244793
+ 496105.531919283 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.553365934 WAKE num_fds:0
+ 496105.553398950 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.553405576 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.553406122 DEV_SLEEP_TIME dev:8 wake:000496105.574733040
+ 496105.553407535 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.574834893 WAKE num_fds:0
+ 496105.574867167 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.574873822 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.574874354 DEV_SLEEP_TIME dev:8 wake:000496105.596201262
+ 496105.574875787 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.596446710 WAKE num_fds:0
+ 496105.596479731 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.596486276 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.596486812 DEV_SLEEP_TIME dev:8 wake:000496105.617813856
+ 496105.596488281 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.618065669 WAKE num_fds:0
+ 496105.618099372 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.618106092 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.618106619 DEV_SLEEP_TIME dev:8 wake:000496105.639433497
+ 496105.618108077 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.639683655 WAKE num_fds:0
+ 496105.639714487 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.639721508 SET_DEV_WAKE dev:8 hw_level:2000 sleep:976
+ 496105.639722054 DEV_SLEEP_TIME dev:8 wake:000496105.660048626
+ 496105.639723477 SLEEP sleep:000000000.020333333 longest_wake:000158140
+ 496105.660301504 WAKE num_fds:0
+ 496105.660334535 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.660367015 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.660367572 DEV_SLEEP_TIME dev:8 wake:000496105.681668625
+ 496105.660369040 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.681942991 WAKE num_fds:0
+ 496105.681976147 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.681982883 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.681983419 DEV_SLEEP_TIME dev:8 wake:000496105.703310237
+ 496105.681984857 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.703558436 WAKE num_fds:0
+ 496105.703591708 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.703598413 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.703598950 DEV_SLEEP_TIME dev:8 wake:000496105.724925813
+ 496105.703600388 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.725176313 WAKE num_fds:0
+ 496105.725208658 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.725215549 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.725216080 DEV_SLEEP_TIME dev:8 wake:000496105.746542803
+ 496105.725217568 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.746792781 WAKE num_fds:0
+ 496105.746825331 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.746831876 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.746832453 DEV_SLEEP_TIME dev:8 wake:000496105.768159431
+ 496105.746833886 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.768409820 WAKE num_fds:0
+ 496105.768442711 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.768449406 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.768449953 DEV_SLEEP_TIME dev:8 wake:000496105.789776891
+ 496105.768451421 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.790025698 WAKE num_fds:0
+ 496105.790058914 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.790065650 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.790066186 DEV_SLEEP_TIME dev:8 wake:000496105.811393034
+ 496105.790067619 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.811643934 WAKE num_fds:0
+ 496105.811676870 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.811683431 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.811683977 DEV_SLEEP_TIME dev:8 wake:000496105.833010970
+ 496105.811685410 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.833259676 WAKE num_fds:0
+ 496105.833293284 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.833299784 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.833300325 DEV_SLEEP_TIME dev:8 wake:000496105.854627374
+ 496105.833301869 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.854876535 WAKE num_fds:0
+ 496105.854909596 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.854916192 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.854916733 DEV_SLEEP_TIME dev:8 wake:000496105.876243686
+ 496105.854918171 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.876365955 WAKE num_fds:0
+ 496105.876403862 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.876410763 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.876411304 DEV_SLEEP_TIME dev:8 wake:000496105.897738112
+ 496105.876412748 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.897985014 WAKE num_fds:0
+ 496105.898017996 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.898024566 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.898025112 DEV_SLEEP_TIME dev:8 wake:000496105.919352085
+ 496105.898026530 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.919602019 WAKE num_fds:0
+ 496105.919635095 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.919641750 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.919642291 DEV_SLEEP_TIME dev:8 wake:000496105.940969270
+ 496105.919643785 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.941221058 WAKE num_fds:0
+ 496105.941253709 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.941260544 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.941261091 DEV_SLEEP_TIME dev:8 wake:000496105.962587808
+ 496105.941262514 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.962838809 WAKE num_fds:0
+ 496105.962871800 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496105.962878330 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.962878866 DEV_SLEEP_TIME dev:8 wake:000496105.984205910
+ 496105.962880375 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496105.984453327 WAKE num_fds:0
+ 496105.984486108 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496105.984492768 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496105.984493239 DEV_SLEEP_TIME dev:8 wake:000496106.005820173
+ 496105.984494638 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.006072327 WAKE num_fds:0
+ 496106.006105654 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.006112179 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.006112715 DEV_SLEEP_TIME dev:8 wake:000496106.027439754
+ 496106.006114163 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.027691130 WAKE num_fds:0
+ 496106.027723174 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.027729900 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.027730446 DEV_SLEEP_TIME dev:8 wake:000496106.049057314
+ 496106.027731899 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.049118332 WAKE num_fds:0
+ 496106.049149760 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496106.049156335 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.049156886 DEV_SLEEP_TIME dev:8 wake:000496106.070483854
+ 496106.049158304 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.070587359 WAKE num_fds:0
+ 496106.070620506 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496106.070627071 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.070627617 DEV_SLEEP_TIME dev:8 wake:000496106.091954571
+ 496106.070629041 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.092200591 WAKE num_fds:0
+ 496106.092233722 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.092240292 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.092240839 DEV_SLEEP_TIME dev:8 wake:000496106.113567797
+ 496106.092242292 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.113817610 WAKE num_fds:0
+ 496106.113850350 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.113857006 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.113857657 DEV_SLEEP_TIME dev:8 wake:000496106.135184485
+ 496106.113859181 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.135434824 WAKE num_fds:0
+ 496106.135468362 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496106.135474907 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.135475448 DEV_SLEEP_TIME dev:8 wake:000496106.156802487
+ 496106.135476881 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.157054420 WAKE num_fds:0
+ 496106.157087412 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.157094092 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.157094628 DEV_SLEEP_TIME dev:8 wake:000496106.178421511
+ 496106.157096102 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.178468120 WAKE num_fds:0
+ 496106.178501477 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496106.178508318 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.178508849 DEV_SLEEP_TIME dev:8 wake:000496106.199835752
+ 496106.178510348 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.200086092 WAKE num_fds:0
+ 496106.200118818 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.200125523 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.200126065 DEV_SLEEP_TIME dev:8 wake:000496106.221452913
+ 496106.200127518 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.221581095 WAKE num_fds:0
+ 496106.221614773 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496106.221621473 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.221621999 DEV_SLEEP_TIME dev:8 wake:000496106.242948877
+ 496106.221623513 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.243200696 WAKE num_fds:0
+ 496106.243234359 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.243241069 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.243241610 DEV_SLEEP_TIME dev:8 wake:000496106.264568453
+ 496106.243243039 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.264635264 WAKE num_fds:0
+ 496106.264667323 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496106.264673953 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.264674489 DEV_SLEEP_TIME dev:8 wake:000496106.286001423
+ 496106.264675948 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.286249247 WAKE num_fds:0
+ 496106.286282017 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.286288618 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.286289159 DEV_SLEEP_TIME dev:8 wake:000496106.307616107
+ 496106.286290592 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.307868020 WAKE num_fds:0
+ 496106.307901131 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.307907807 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.307908353 DEV_SLEEP_TIME dev:8 wake:000496106.329235196
+ 496106.307909776 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.329485164 WAKE num_fds:0
+ 496106.329518181 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496106.329525026 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.329525563 DEV_SLEEP_TIME dev:8 wake:000496106.350852295
+ 496106.329527071 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.350909770 WAKE num_fds:0
+ 496106.350941839 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.350948820 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.350949357 DEV_SLEEP_TIME dev:8 wake:000496106.372276054
+ 496106.350950795 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.372524735 WAKE num_fds:0
+ 496106.372558297 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496106.372564937 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.372565469 DEV_SLEEP_TIME dev:8 wake:000496106.393892407
+ 496106.372566922 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.394139539 WAKE num_fds:0
+ 496106.394172305 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.394178965 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.394179517 DEV_SLEEP_TIME dev:8 wake:000496106.415506450
+ 496106.394180940 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.415761560 WAKE num_fds:0
+ 496106.415794401 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.415801106 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.415801663 DEV_SLEEP_TIME dev:8 wake:000496106.437128511
+ 496106.415803201 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.437377767 WAKE num_fds:0
+ 496106.437410964 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496106.437417664 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.437418206 DEV_SLEEP_TIME dev:8 wake:000496106.458745059
+ 496106.437419634 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.458813434 WAKE num_fds:0
+ 496106.458845648 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.458852308 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.458852850 DEV_SLEEP_TIME dev:8 wake:000496106.480179718
+ 496106.458854293 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.480280397 WAKE num_fds:0
+ 496106.480312832 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496106.480319598 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.480320129 DEV_SLEEP_TIME dev:8 wake:000496106.501646992
+ 496106.480321598 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.501898760 WAKE num_fds:0
+ 496106.501932046 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.501938717 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.501939258 DEV_SLEEP_TIME dev:8 wake:000496106.523266191
+ 496106.501940681 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.523515268 WAKE num_fds:0
+ 496106.523547652 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496106.523554283 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.523554824 DEV_SLEEP_TIME dev:8 wake:000496106.544881747
+ 496106.523556262 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.545129010 WAKE num_fds:0
+ 496106.545161906 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.545168631 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.545169168 DEV_SLEEP_TIME dev:8 wake:000496106.566495986
+ 496106.545170621 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.566746781 WAKE num_fds:0
+ 496106.566779802 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.566786387 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.566786938 DEV_SLEEP_TIME dev:8 wake:000496106.588113892
+ 496106.566788472 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.588381170 WAKE num_fds:0
+ 496106.588413956 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496106.588420536 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.588421082 DEV_SLEEP_TIME dev:8 wake:000496106.609748050
+ 496106.588422505 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.609997674 WAKE num_fds:0
+ 496106.610030184 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.610036814 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.610037365 DEV_SLEEP_TIME dev:8 wake:000496106.631364289
+ 496106.610038794 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.631612047 WAKE num_fds:0
+ 496106.631644993 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496106.631651568 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.631652109 DEV_SLEEP_TIME dev:8 wake:000496106.652979113
+ 496106.631653543 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.653079141 WAKE num_fds:0
+ 496106.653111641 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.653118306 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.653118862 DEV_SLEEP_TIME dev:8 wake:000496106.674445796
+ 496106.653120301 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.674531169 WAKE num_fds:0
+ 496106.674566811 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496106.674573477 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.674574018 DEV_SLEEP_TIME dev:8 wake:000496106.695900961
+ 496106.674575446 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.696158187 WAKE num_fds:0
+ 496106.696191840 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.696198555 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.696199092 DEV_SLEEP_TIME dev:8 wake:000496106.717525935
+ 496106.696201021 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.717774134 WAKE num_fds:0
+ 496106.717807591 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496106.717814186 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.717814727 DEV_SLEEP_TIME dev:8 wake:000496106.739141681
+ 496106.717816196 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.739392065 WAKE num_fds:0
+ 496106.739426254 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496106.739432934 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496106.739433591 DEV_SLEEP_TIME dev:8 wake:000496106.760760369
+ 496106.739435179 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496106.760007023 WAKE num_fds:1
+ 496106.760016440 PB_MSG msg_id:1
+ 496106.760018008 DEV_REMOVED dev:8
+ 496106.760032377 SLEEP sleep:000000000.000000000 longest_wake:000158140
+ 496118.354002034 WAKE num_fds:1
+ 496118.354013360 PB_MSG msg_id:0
+ 496118.354015856 DEV_ADDED dev:8
+ 496118.354055002 ODEV_NO_STREAMS dev:8 hw_level:0 write:2048
+ 496118.354057131 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496118.354057663 DEV_SLEEP_TIME dev:8 wake:000496118.375389076
+ 496118.354058795 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496118.354063185 WAKE num_fds:1
+ 496118.354066032 PB_MSG msg_id:2
+ 496118.354066598 WRITE_STREAMS_WAIT stream:140000
+ 496118.354078641 STREAM_ADDED id:140000 dev:8
+ 496118.354085291 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2048
+ 496118.354096267 FILL_AUDIO dev:8 hw_level:2048
+ 496118.354097760 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.354098121 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.354098893 WRITE_STREAMS_MIXED write_limit:0
+ 496118.354111888 FILL_AUDIO_DONE hw_level:2048 total_written:0 min_cb_level:1024
+ 496118.354120337 SET_DEV_WAKE dev:8 hw_level:2048 sleep:2048
+ 496118.354120738 DEV_SLEEP_TIME dev:8 wake:000496118.396779331
+ 496118.354121530 SLEEP sleep:000000000.042666666 longest_wake:000158140
+ 496118.354648175 WAKE num_fds:1
+ 496118.354678825 FILL_AUDIO dev:8 hw_level:2048
+ 496118.354682173 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.354690728 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.354702069 DEV_STREAM_MIX written:1024 read:1024
+ 496118.354703026 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.354705141 FILL_AUDIO_DONE hw_level:2048 total_written:1024 min_cb_level:1024
+ 496118.354706975 STREAM_SLEEP_TIME id:140000 wake:000496118.375409268
+ 496118.354714473 SET_DEV_WAKE dev:8 hw_level:3072 sleep:3072
+ 496118.354714989 DEV_SLEEP_TIME dev:8 wake:000496118.418706179
+ 496118.354716537 SLEEP sleep:000000000.020703089 longest_wake:000158140
+ 496118.375445608 WAKE num_fds:0
+ 496118.375470776 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496118.375493288 FILL_AUDIO dev:8 hw_level:2208
+ 496118.375498029 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.375498460 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.375499492 WRITE_STREAMS_MIXED write_limit:0
+ 496118.375502529 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496118.375511756 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496118.375512242 DEV_SLEEP_TIME dev:8 wake:000496118.421503923
+ 496118.375514156 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496118.375625007 WAKE num_fds:1
+ 496118.375650521 FILL_AUDIO dev:8 hw_level:2208
+ 496118.375652766 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.375665305 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.375680355 DEV_STREAM_MIX written:1024 read:1024
+ 496118.375681277 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.375683131 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496118.375684780 STREAM_SLEEP_TIME id:140000 wake:000496118.396742601
+ 496118.375692087 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496118.375692633 DEV_SLEEP_TIME dev:8 wake:000496118.443017306
+ 496118.375694217 SLEEP sleep:000000000.021058628 longest_wake:000158140
+ 496118.396778232 WAKE num_fds:0
+ 496118.396804492 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496118.396830803 FILL_AUDIO dev:8 hw_level:2176
+ 496118.396835373 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.396835890 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.396837007 WRITE_STREAMS_MIXED write_limit:0
+ 496118.396839979 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496118.396848248 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496118.396848825 DEV_SLEEP_TIME dev:8 wake:000496118.442174490
+ 496118.396850488 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496118.396934603 WAKE num_fds:1
+ 496118.396958914 FILL_AUDIO dev:8 hw_level:2176
+ 496118.396961299 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.396978694 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.396986317 DEV_STREAM_MIX written:1024 read:1024
+ 496118.396987114 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.396989123 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496118.396990582 STREAM_SLEEP_TIME id:140000 wake:000496118.418075934
+ 496118.396997628 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496118.396998149 DEV_SLEEP_TIME dev:8 wake:000496118.463656556
+ 496118.396999758 SLEEP sleep:000000000.021086044 longest_wake:000158140
+ 496118.418106179 WAKE num_fds:0
+ 496118.418127885 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496118.418148733 FILL_AUDIO dev:8 hw_level:2192
+ 496118.418152962 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.418153584 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.418154666 WRITE_STREAMS_MIXED write_limit:0
+ 496118.418157608 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496118.418166053 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496118.418166554 DEV_SLEEP_TIME dev:8 wake:000496118.463825722
+ 496118.418168558 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496118.418266114 WAKE num_fds:1
+ 496118.418294374 FILL_AUDIO dev:8 hw_level:2192
+ 496118.418296063 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.418306216 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.418312010 DEV_STREAM_MIX written:1024 read:1024
+ 496118.418312576 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.418313588 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496118.418314871 STREAM_SLEEP_TIME id:140000 wake:000496118.439409267
+ 496118.418320720 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496118.418321136 DEV_SLEEP_TIME dev:8 wake:000496118.485314360
+ 496118.418322293 SLEEP sleep:000000000.021094907 longest_wake:000158140
+ 496118.439496761 WAKE num_fds:0
+ 496118.439530519 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496118.439586553 FILL_AUDIO dev:8 hw_level:2208
+ 496118.439591966 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.439592507 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.439593695 WRITE_STREAMS_MIXED write_limit:0
+ 496118.439597258 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496118.439605853 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496118.439606354 DEV_SLEEP_TIME dev:8 wake:000496118.485598576
+ 496118.439608218 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496118.439716288 WAKE num_fds:1
+ 496118.439750747 FILL_AUDIO dev:8 hw_level:2208
+ 496118.439752842 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.439767887 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.439772748 DEV_STREAM_MIX written:1024 read:1024
+ 496118.439773409 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.439774372 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496118.439775434 STREAM_SLEEP_TIME id:140000 wake:000496118.460742600
+ 496118.439781538 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496118.439781894 DEV_SLEEP_TIME dev:8 wake:000496118.507108286
+ 496118.439782906 SLEEP sleep:000000000.020967647 longest_wake:000158140
+ 496118.460789142 WAKE num_fds:0
+ 496118.460810982 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496118.460836150 FILL_AUDIO dev:8 hw_level:2176
+ 496118.460840610 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.460841127 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.460842124 WRITE_STREAMS_MIXED write_limit:0
+ 496118.460844995 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496118.460853270 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496118.460853771 DEV_SLEEP_TIME dev:8 wake:000496118.506179516
+ 496118.460855876 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496118.460930107 WAKE num_fds:1
+ 496118.460954508 FILL_AUDIO dev:8 hw_level:2176
+ 496118.460955716 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.460966080 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.460971678 DEV_STREAM_MIX written:1024 read:1024
+ 496118.460972239 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.460973136 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496118.460974234 STREAM_SLEEP_TIME id:140000 wake:000496118.482075933
+ 496118.460979857 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496118.460980238 DEV_SLEEP_TIME dev:8 wake:000496118.527640454
+ 496118.460981235 SLEEP sleep:000000000.021102145 longest_wake:000158140
+ 496118.482113920 WAKE num_fds:0
+ 496118.482135974 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496118.482159569 FILL_AUDIO dev:8 hw_level:2192
+ 496118.482163130 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.482163491 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.482164624 WRITE_STREAMS_MIXED write_limit:0
+ 496118.482167216 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496118.482175522 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496118.482175939 DEV_SLEEP_TIME dev:8 wake:000496118.527835011
+ 496118.482177634 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496118.482244261 WAKE num_fds:1
+ 496118.482270354 FILL_AUDIO dev:8 hw_level:2192
+ 496118.482271744 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.482283118 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.482287734 DEV_STREAM_MIX written:1024 read:1024
+ 496118.482288242 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.482289425 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496118.482290547 STREAM_SLEEP_TIME id:140000 wake:000496118.503409266
+ 496118.482297115 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496118.482297491 DEV_SLEEP_TIME dev:8 wake:000496118.549289966
+ 496118.482298539 SLEEP sleep:000000000.021119300 longest_wake:000158140
+ 496118.503438723 WAKE num_fds:0
+ 496118.503461769 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496118.503484060 FILL_AUDIO dev:8 hw_level:2208
+ 496118.503488733 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.503489426 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.503490351 WRITE_STREAMS_MIXED write_limit:0
+ 496118.503493748 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496118.503503115 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496118.503503465 DEV_SLEEP_TIME dev:8 wake:000496118.549495009
+ 496118.503505371 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496118.503555888 WAKE num_fds:1
+ 496118.503581022 FILL_AUDIO dev:8 hw_level:2208
+ 496118.503582739 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.503592868 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.503597479 DEV_STREAM_MIX written:1024 read:1024
+ 496118.503598142 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.503599527 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496118.503600467 STREAM_SLEEP_TIME id:140000 wake:000496118.524742599
+ 496118.503605592 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496118.503605971 DEV_SLEEP_TIME dev:8 wake:000496118.570933428
+ 496118.503607064 SLEEP sleep:000000000.021142504 longest_wake:000158140
+ 496118.524771187 WAKE num_fds:0
+ 496118.524792444 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496118.524811297 FILL_AUDIO dev:8 hw_level:2224
+ 496118.524814347 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.524814633 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.524815430 WRITE_STREAMS_MIXED write_limit:0
+ 496118.524817932 FILL_AUDIO_DONE hw_level:2224 total_written:0 min_cb_level:1024
+ 496118.524825913 SET_DEV_WAKE dev:8 hw_level:2224 sleep:2224
+ 496118.524826242 DEV_SLEEP_TIME dev:8 wake:000496118.571152173
+ 496118.524827565 SLEEP sleep:000000000.046333333 longest_wake:000158140
+ 496118.524879030 WAKE num_fds:1
+ 496118.524900851 FILL_AUDIO dev:8 hw_level:2176
+ 496118.524902685 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.524911230 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.524915787 DEV_STREAM_MIX written:1024 read:1024
+ 496118.524916319 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.524917543 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496118.524918588 STREAM_SLEEP_TIME id:140000 wake:000496118.546075932
+ 496118.524924426 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496118.524924899 DEV_SLEEP_TIME dev:8 wake:000496118.591584778
+ 496118.524925885 SLEEP sleep:000000000.021157820 longest_wake:000158140
+ 496118.546097655 WAKE num_fds:0
+ 496118.546112541 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496118.546126703 FILL_AUDIO dev:8 hw_level:2192
+ 496118.546128925 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.546129092 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.546129613 WRITE_STREAMS_MIXED write_limit:0
+ 496118.546130952 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496118.546136209 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496118.546136370 DEV_SLEEP_TIME dev:8 wake:000496118.591798078
+ 496118.546137203 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496118.546161324 WAKE num_fds:1
+ 496118.546173430 FILL_AUDIO dev:8 hw_level:2192
+ 496118.546174217 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.546179310 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.546183028 DEV_STREAM_MIX written:1024 read:1024
+ 496118.546183368 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.546183858 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496118.546184331 STREAM_SLEEP_TIME id:140000 wake:000496118.567409265
+ 496118.546189248 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496118.546189390 DEV_SLEEP_TIME dev:8 wake:000496118.613184163
+ 496118.546189920 SLEEP sleep:000000000.021225102 longest_wake:000158140
+ 496118.567424239 WAKE num_fds:0
+ 496118.567439411 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496118.567452286 FILL_AUDIO dev:8 hw_level:2208
+ 496118.567454872 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.567455174 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.567455825 WRITE_STREAMS_MIXED write_limit:0
+ 496118.567457476 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496118.567464131 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496118.567464314 DEV_SLEEP_TIME dev:8 wake:000496118.613458159
+ 496118.567465126 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496118.567490875 WAKE num_fds:1
+ 496118.567505725 FILL_AUDIO dev:8 hw_level:2208
+ 496118.567506502 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.567511232 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.567512996 DEV_STREAM_MIX written:1024 read:1024
+ 496118.567513203 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.567513727 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496118.567514129 STREAM_SLEEP_TIME id:140000 wake:000496118.588742598
+ 496118.567518840 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496118.567518992 DEV_SLEEP_TIME dev:8 wake:000496118.634847296
+ 496118.567519449 SLEEP sleep:000000000.021228635 longest_wake:000158140
+ 496118.588780218 WAKE num_fds:0
+ 496118.588794235 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496118.588810187 FILL_AUDIO dev:8 hw_level:2224
+ 496118.588812252 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.588812393 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.588812942 WRITE_STREAMS_MIXED write_limit:0
+ 496118.588814561 FILL_AUDIO_DONE hw_level:2224 total_written:0 min_cb_level:1024
+ 496118.588819039 SET_DEV_WAKE dev:8 hw_level:2224 sleep:2224
+ 496118.588819202 DEV_SLEEP_TIME dev:8 wake:000496118.635148443
+ 496118.588820036 SLEEP sleep:000000000.046333333 longest_wake:000158140
+ 496118.588854210 WAKE num_fds:1
+ 496118.588870487 FILL_AUDIO dev:8 hw_level:2176
+ 496118.588871082 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.588877727 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.588880046 DEV_STREAM_MIX written:1024 read:1024
+ 496118.588880375 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.588881027 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496118.588881474 STREAM_SLEEP_TIME id:140000 wake:000496118.610075931
+ 496118.588886516 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496118.588886664 DEV_SLEEP_TIME dev:8 wake:000496118.655547937
+ 496118.588887092 SLEEP sleep:000000000.021194660 longest_wake:000158140
+ 496118.610097814 WAKE num_fds:0
+ 496118.610123399 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496118.610137913 FILL_AUDIO dev:8 hw_level:2192
+ 496118.610140374 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.610140519 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.610141505 WRITE_STREAMS_MIXED write_limit:0
+ 496118.610143585 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496118.610149415 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496118.610149652 DEV_SLEEP_TIME dev:8 wake:000496118.655810785
+ 496118.610150372 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496118.610181468 WAKE num_fds:1
+ 496118.610197004 FILL_AUDIO dev:8 hw_level:2192
+ 496118.610198230 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.610203984 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.610206515 DEV_STREAM_MIX written:1024 read:1024
+ 496118.610206788 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.610207314 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496118.610207787 STREAM_SLEEP_TIME id:140000 wake:000496118.631409264
+ 496118.610212963 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496118.610213165 DEV_SLEEP_TIME dev:8 wake:000496118.677207596
+ 496118.610213722 SLEEP sleep:000000000.021201668 longest_wake:000158140
+ 496118.631437687 WAKE num_fds:0
+ 496118.631451138 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496118.631464477 FILL_AUDIO dev:8 hw_level:2208
+ 496118.631466576 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.631466740 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.631467215 WRITE_STREAMS_MIXED write_limit:0
+ 496118.631468559 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496118.631474164 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496118.631474394 DEV_SLEEP_TIME dev:8 wake:000496118.677468970
+ 496118.631475094 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496118.631557823 WAKE num_fds:1
+ 496118.631572529 FILL_AUDIO dev:8 hw_level:2208
+ 496118.631573346 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.631578696 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.631580226 DEV_STREAM_MIX written:1024 read:1024
+ 496118.631580423 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.631581039 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496118.631581413 STREAM_SLEEP_TIME id:140000 wake:000496118.652742597
+ 496118.631585882 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496118.631585996 DEV_SLEEP_TIME dev:8 wake:000496118.698914565
+ 496118.631586399 SLEEP sleep:000000000.021161365 longest_wake:000158140
+ 496118.652804884 WAKE num_fds:0
+ 496118.652853602 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496118.652906300 FILL_AUDIO dev:8 hw_level:2176
+ 496118.652916970 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.652917116 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.652918057 WRITE_STREAMS_MIXED write_limit:0
+ 496118.652923991 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496118.652949242 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496118.652950075 DEV_SLEEP_TIME dev:8 wake:000496118.698258600
+ 496118.652951721 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496118.653034042 WAKE num_fds:1
+ 496118.653082023 FILL_AUDIO dev:8 hw_level:2176
+ 496118.653087608 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.653115701 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.653126099 DEV_STREAM_MIX written:1024 read:1024
+ 496118.653126366 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.653128297 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496118.653129600 STREAM_SLEEP_TIME id:140000 wake:000496118.674075930
+ 496118.653141084 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496118.653141227 DEV_SLEEP_TIME dev:8 wake:000496118.719796011
+ 496118.653142617 SLEEP sleep:000000000.020946585 longest_wake:000158140
+ 496118.674106400 WAKE num_fds:0
+ 496118.674120878 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496118.674135054 FILL_AUDIO dev:8 hw_level:2192
+ 496118.674137010 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.674137173 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.674137648 WRITE_STREAMS_MIXED write_limit:0
+ 496118.674139240 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496118.674144657 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496118.674144812 DEV_SLEEP_TIME dev:8 wake:000496118.719806407
+ 496118.674145446 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496118.674178767 WAKE num_fds:1
+ 496118.674190201 FILL_AUDIO dev:8 hw_level:2192
+ 496118.674190857 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.674195897 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.674197446 DEV_STREAM_MIX written:1024 read:1024
+ 496118.674197623 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.674197903 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496118.674198238 STREAM_SLEEP_TIME id:140000 wake:000496118.695409263
+ 496118.674202644 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496118.674202766 DEV_SLEEP_TIME dev:8 wake:000496118.741198093
+ 496118.674203141 SLEEP sleep:000000000.021211170 longest_wake:000158140
+ 496118.695425973 WAKE num_fds:0
+ 496118.695442160 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496118.695456413 FILL_AUDIO dev:8 hw_level:2208
+ 496118.695458598 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.695458801 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.695459398 WRITE_STREAMS_MIXED write_limit:0
+ 496118.695461090 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496118.695466757 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496118.695466949 DEV_SLEEP_TIME dev:8 wake:000496118.741461697
+ 496118.695467723 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496118.695503662 WAKE num_fds:1
+ 496118.695518956 FILL_AUDIO dev:8 hw_level:2208
+ 496118.695519793 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.695526046 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.695528211 DEV_STREAM_MIX written:1024 read:1024
+ 496118.695528463 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.695528914 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496118.695529424 STREAM_SLEEP_TIME id:140000 wake:000496118.716742596
+ 496118.695534156 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496118.695534347 DEV_SLEEP_TIME dev:8 wake:000496118.762862556
+ 496118.695534780 SLEEP sleep:000000000.021213373 longest_wake:000158140
+ 496118.716796026 WAKE num_fds:0
+ 496118.716844186 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496118.716881094 FILL_AUDIO dev:8 hw_level:2176
+ 496118.716892655 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.716892910 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.716894646 WRITE_STREAMS_MIXED write_limit:0
+ 496118.716899983 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496118.716910139 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496118.716910410 DEV_SLEEP_TIME dev:8 wake:000496118.762235198
+ 496118.716914465 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496118.717037504 WAKE num_fds:1
+ 496118.717069219 FILL_AUDIO dev:8 hw_level:2176
+ 496118.717073251 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.717093009 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.717099155 DEV_STREAM_MIX written:1024 read:1024
+ 496118.717099507 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.717100701 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496118.717101460 STREAM_SLEEP_TIME id:140000 wake:000496118.738075929
+ 496118.717107009 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496118.717107210 DEV_SLEEP_TIME dev:8 wake:000496118.783767828
+ 496118.717109169 SLEEP sleep:000000000.020974767 longest_wake:000158140
+ 496118.738099922 WAKE num_fds:0
+ 496118.738113821 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496118.738128082 FILL_AUDIO dev:8 hw_level:2192
+ 496118.738130192 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.738130387 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.738130972 WRITE_STREAMS_MIXED write_limit:0
+ 496118.738132446 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496118.738137824 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496118.738138054 DEV_SLEEP_TIME dev:8 wake:000496118.783799661
+ 496118.738138719 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496118.738174290 WAKE num_fds:1
+ 496118.738189554 FILL_AUDIO dev:8 hw_level:2192
+ 496118.738190236 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.738195073 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.738198050 DEV_STREAM_MIX written:1024 read:1024
+ 496118.738198380 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.738198938 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496118.738199357 STREAM_SLEEP_TIME id:140000 wake:000496118.759409262
+ 496118.738203929 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496118.738204062 DEV_SLEEP_TIME dev:8 wake:000496118.805199158
+ 496118.738204447 SLEEP sleep:000000000.021210104 longest_wake:000158140
+ 496118.759429248 WAKE num_fds:0
+ 496118.759443563 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496118.759460244 FILL_AUDIO dev:8 hw_level:2208
+ 496118.759462267 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.759462398 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.759462818 WRITE_STREAMS_MIXED write_limit:0
+ 496118.759464345 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496118.759469501 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496118.759469655 DEV_SLEEP_TIME dev:8 wake:000496118.805464831
+ 496118.759470367 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496118.759517748 WAKE num_fds:1
+ 496118.759531678 FILL_AUDIO dev:8 hw_level:2208
+ 496118.759532357 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.759537800 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.759540617 DEV_STREAM_MIX written:1024 read:1024
+ 496118.759540905 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.759541444 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496118.759541852 STREAM_SLEEP_TIME id:140000 wake:000496118.780742595
+ 496118.759546251 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496118.759546357 DEV_SLEEP_TIME dev:8 wake:000496118.826875025
+ 496118.759546696 SLEEP sleep:000000000.021200903 longest_wake:000158140
+ 496118.780759760 WAKE num_fds:0
+ 496118.780775008 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496118.780790131 FILL_AUDIO dev:8 hw_level:2224
+ 496118.780791975 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.780792123 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.780792803 WRITE_STREAMS_MIXED write_limit:0
+ 496118.780793974 FILL_AUDIO_DONE hw_level:2224 total_written:0 min_cb_level:1024
+ 496118.780800286 SET_DEV_WAKE dev:8 hw_level:2224 sleep:2224
+ 496118.780800421 DEV_SLEEP_TIME dev:8 wake:000496118.827128100
+ 496118.780801289 SLEEP sleep:000000000.046333333 longest_wake:000158140
+ 496118.780847586 WAKE num_fds:1
+ 496118.780862518 FILL_AUDIO dev:8 hw_level:2176
+ 496118.780863379 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.780868464 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.780870041 DEV_STREAM_MIX written:1024 read:1024
+ 496118.780870230 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.780870593 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496118.780870958 STREAM_SLEEP_TIME id:140000 wake:000496118.802075928
+ 496118.780875355 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496118.780875478 DEV_SLEEP_TIME dev:8 wake:000496118.847537449
+ 496118.780875847 SLEEP sleep:000000000.021205145 longest_wake:000158140
+ 496118.802102780 WAKE num_fds:0
+ 496118.802120472 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496118.802137210 FILL_AUDIO dev:8 hw_level:2192
+ 496118.802139917 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.802140064 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.802140609 WRITE_STREAMS_MIXED write_limit:0
+ 496118.802142432 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496118.802147701 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496118.802147852 DEV_SLEEP_TIME dev:8 wake:000496118.847809557
+ 496118.802148516 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496118.802195380 WAKE num_fds:1
+ 496118.802209967 FILL_AUDIO dev:8 hw_level:2192
+ 496118.802210703 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.802217788 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.802219789 DEV_STREAM_MIX written:1024 read:1024
+ 496118.802219989 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.802220449 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496118.802220872 STREAM_SLEEP_TIME id:140000 wake:000496118.823409261
+ 496118.802225303 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496118.802225418 DEV_SLEEP_TIME dev:8 wake:000496118.869220669
+ 496118.802225820 SLEEP sleep:000000000.021188592 longest_wake:000158140
+ 496118.823426698 WAKE num_fds:0
+ 496118.823441151 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496118.823457708 FILL_AUDIO dev:8 hw_level:2208
+ 496118.823460089 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.823460330 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.823460828 WRITE_STREAMS_MIXED write_limit:0
+ 496118.823462430 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496118.823467790 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496118.823467949 DEV_SLEEP_TIME dev:8 wake:000496118.869462918
+ 496118.823468618 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496118.823507440 WAKE num_fds:1
+ 496118.823521661 FILL_AUDIO dev:8 hw_level:2208
+ 496118.823522559 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.823529596 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.823531920 DEV_STREAM_MIX written:1024 read:1024
+ 496118.823532244 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.823533007 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496118.823533383 STREAM_SLEEP_TIME id:140000 wake:000496118.844742594
+ 496118.823538141 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496118.823538313 DEV_SLEEP_TIME dev:8 wake:000496118.890866568
+ 496118.823538782 SLEEP sleep:000000000.021209359 longest_wake:000158140
+ 496118.844793611 WAKE num_fds:0
+ 496118.844842455 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496118.844888762 FILL_AUDIO dev:8 hw_level:2176
+ 496118.844899355 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.844899489 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.844901921 WRITE_STREAMS_MIXED write_limit:0
+ 496118.844909466 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496118.844922258 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496118.844923289 DEV_SLEEP_TIME dev:8 wake:000496118.890245017
+ 496118.844926573 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496118.845050151 WAKE num_fds:1
+ 496118.845096292 FILL_AUDIO dev:8 hw_level:2176
+ 496118.845101172 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.845127890 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.845137332 DEV_STREAM_MIX written:1024 read:1024
+ 496118.845138226 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.845140015 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496118.845143090 STREAM_SLEEP_TIME id:140000 wake:000496118.866075927
+ 496118.845154493 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496118.845155261 DEV_SLEEP_TIME dev:8 wake:000496118.911809504
+ 496118.845156233 SLEEP sleep:000000000.020933089 longest_wake:000158140
+ 496118.866103566 WAKE num_fds:0
+ 496118.866119526 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496118.866134595 FILL_AUDIO dev:8 hw_level:2192
+ 496118.866137519 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.866137677 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.866138149 WRITE_STREAMS_MIXED write_limit:0
+ 496118.866140088 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496118.866146294 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496118.866146511 DEV_SLEEP_TIME dev:8 wake:000496118.911807282
+ 496118.866147537 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496118.866177435 WAKE num_fds:1
+ 496118.866192557 FILL_AUDIO dev:8 hw_level:2192
+ 496118.866193498 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.866198506 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.866200264 DEV_STREAM_MIX written:1024 read:1024
+ 496118.866200426 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.866200819 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496118.866201185 STREAM_SLEEP_TIME id:140000 wake:000496118.887409260
+ 496118.866205681 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496118.866205793 DEV_SLEEP_TIME dev:8 wake:000496118.933201012
+ 496118.866206313 SLEEP sleep:000000000.021208248 longest_wake:000158140
+ 496118.887425173 WAKE num_fds:0
+ 496118.887438794 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496118.887455874 FILL_AUDIO dev:8 hw_level:2208
+ 496118.887457788 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.887457958 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.887458388 WRITE_STREAMS_MIXED write_limit:0
+ 496118.887460020 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496118.887465184 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496118.887465331 DEV_SLEEP_TIME dev:8 wake:000496118.933460516
+ 496118.887465992 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496118.887502924 WAKE num_fds:1
+ 496118.887517026 FILL_AUDIO dev:8 hw_level:2208
+ 496118.887517553 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.887522198 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.887524704 DEV_STREAM_MIX written:1024 read:1024
+ 496118.887524986 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.887525564 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496118.887525976 STREAM_SLEEP_TIME id:140000 wake:000496118.908742593
+ 496118.887530536 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496118.887530658 DEV_SLEEP_TIME dev:8 wake:000496118.954859148
+ 496118.887531007 SLEEP sleep:000000000.021216778 longest_wake:000158140
+ 496118.908761014 WAKE num_fds:0
+ 496118.908774255 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496118.908788078 FILL_AUDIO dev:8 hw_level:2224
+ 496118.908791095 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.908791227 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.908791643 WRITE_STREAMS_MIXED write_limit:0
+ 496118.908792879 FILL_AUDIO_DONE hw_level:2224 total_written:0 min_cb_level:1024
+ 496118.908798303 SET_DEV_WAKE dev:8 hw_level:2224 sleep:2224
+ 496118.908798448 DEV_SLEEP_TIME dev:8 wake:000496118.955126660
+ 496118.908799066 SLEEP sleep:000000000.046333333 longest_wake:000158140
+ 496118.908840065 WAKE num_fds:1
+ 496118.908854501 FILL_AUDIO dev:8 hw_level:2176
+ 496118.908855141 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.908859990 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.908861559 DEV_STREAM_MIX written:1024 read:1024
+ 496118.908861758 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.908862178 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496118.908862536 STREAM_SLEEP_TIME id:140000 wake:000496118.930075926
+ 496118.908866946 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496118.908867060 DEV_SLEEP_TIME dev:8 wake:000496118.975529057
+ 496118.908867432 SLEEP sleep:000000000.021213535 longest_wake:000158140
+ 496118.930094024 WAKE num_fds:0
+ 496118.930107968 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496118.930124375 FILL_AUDIO dev:8 hw_level:2192
+ 496118.930126805 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.930127218 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.930127938 WRITE_STREAMS_MIXED write_limit:0
+ 496118.930129478 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496118.930136050 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496118.930136345 DEV_SLEEP_TIME dev:8 wake:000496118.975796868
+ 496118.930137375 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496118.930180425 WAKE num_fds:1
+ 496118.930195856 FILL_AUDIO dev:8 hw_level:2192
+ 496118.930197015 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.930202916 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.930204953 DEV_STREAM_MIX written:1024 read:1024
+ 496118.930205305 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.930205985 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496118.930206375 STREAM_SLEEP_TIME id:140000 wake:000496118.951409259
+ 496118.930211062 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496118.930211211 DEV_SLEEP_TIME dev:8 wake:000496118.997206196
+ 496118.930211579 SLEEP sleep:000000000.021203063 longest_wake:000158140
+ 496118.951427153 WAKE num_fds:0
+ 496118.951440867 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496118.951457640 FILL_AUDIO dev:8 hw_level:2208
+ 496118.951459818 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.951459979 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.951460418 WRITE_STREAMS_MIXED write_limit:0
+ 496118.951462188 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496118.951468096 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496118.951468253 DEV_SLEEP_TIME dev:8 wake:000496118.997462723
+ 496118.951469025 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496118.951511365 WAKE num_fds:1
+ 496118.951527108 FILL_AUDIO dev:8 hw_level:2208
+ 496118.951527922 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.951533170 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.951534775 DEV_STREAM_MIX written:1024 read:1024
+ 496118.951534973 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.951536328 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496118.951536704 STREAM_SLEEP_TIME id:140000 wake:000496118.972742592
+ 496118.951541447 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496118.951541573 DEV_SLEEP_TIME dev:8 wake:000496119.018869867
+ 496118.951542050 SLEEP sleep:000000000.021206058 longest_wake:000158140
+ 496118.972761770 WAKE num_fds:0
+ 496118.972776276 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496118.972797345 FILL_AUDIO dev:8 hw_level:2224
+ 496118.972803518 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.972803884 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.972804436 WRITE_STREAMS_MIXED write_limit:0
+ 496118.972806221 FILL_AUDIO_DONE hw_level:2224 total_written:0 min_cb_level:1024
+ 496118.972822039 SET_DEV_WAKE dev:8 hw_level:2224 sleep:2224
+ 496118.972822283 DEV_SLEEP_TIME dev:8 wake:000496119.019140179
+ 496118.972823594 SLEEP sleep:000000000.046333333 longest_wake:000158140
+ 496118.972882579 WAKE num_fds:1
+ 496118.972898523 FILL_AUDIO dev:8 hw_level:2176
+ 496118.972899309 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.972905056 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.972906877 DEV_STREAM_MIX written:1024 read:1024
+ 496118.972907047 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.972907575 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496118.972907888 STREAM_SLEEP_TIME id:140000 wake:000496118.994075925
+ 496118.972912755 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496118.972912878 DEV_SLEEP_TIME dev:8 wake:000496119.039574389
+ 496118.972913362 SLEEP sleep:000000000.021168202 longest_wake:000158140
+ 496118.994114414 WAKE num_fds:0
+ 496118.994140794 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496118.994156916 FILL_AUDIO dev:8 hw_level:2192
+ 496118.994159491 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496118.994159650 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496118.994160169 WRITE_STREAMS_MIXED write_limit:0
+ 496118.994164191 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496118.994171200 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496118.994171359 DEV_SLEEP_TIME dev:8 wake:000496119.039831814
+ 496118.994172168 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496118.994205388 WAKE num_fds:1
+ 496118.994222444 FILL_AUDIO dev:8 hw_level:2192
+ 496118.994223295 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496118.994229362 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496118.994232914 DEV_STREAM_MIX written:1024 read:1024
+ 496118.994233254 WRITE_STREAMS_MIXED write_limit:1024
+ 496118.994233932 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496118.994234486 STREAM_SLEEP_TIME id:140000 wake:000496119.015409258
+ 496118.994239352 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496118.994239573 DEV_SLEEP_TIME dev:8 wake:000496119.061234321
+ 496118.994239966 SLEEP sleep:000000000.021174937 longest_wake:000158140
+ 496119.015494273 WAKE num_fds:0
+ 496119.015517241 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496119.015543933 FILL_AUDIO dev:8 hw_level:2208
+ 496119.015548213 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.015548684 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.015549922 WRITE_STREAMS_MIXED write_limit:0
+ 496119.015552888 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496119.015562300 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496119.015563077 DEV_SLEEP_TIME dev:8 wake:000496119.061554262
+ 496119.015565252 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496119.015647747 WAKE num_fds:1
+ 496119.015677927 FILL_AUDIO dev:8 hw_level:2208
+ 496119.015679811 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.015695257 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.015700629 DEV_STREAM_MIX written:1024 read:1024
+ 496119.015701276 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.015702835 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496119.015704443 STREAM_SLEEP_TIME id:140000 wake:000496119.036742591
+ 496119.015711715 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496119.015712136 DEV_SLEEP_TIME dev:8 wake:000496119.083037125
+ 496119.015713529 SLEEP sleep:000000000.021038799 longest_wake:000158140
+ 496119.036811692 WAKE num_fds:0
+ 496119.036832364 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496119.036857117 FILL_AUDIO dev:8 hw_level:2176
+ 496119.036861557 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.036862023 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.036863201 WRITE_STREAMS_MIXED write_limit:0
+ 496119.036866368 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496119.036875123 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496119.036875629 DEV_SLEEP_TIME dev:8 wake:000496119.082201545
+ 496119.036877418 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496119.036947164 WAKE num_fds:1
+ 496119.036973731 FILL_AUDIO dev:8 hw_level:2176
+ 496119.036975349 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.036987853 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.036993702 DEV_STREAM_MIX written:1024 read:1024
+ 496119.036994293 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.036995441 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496119.036996538 STREAM_SLEEP_TIME id:140000 wake:000496119.058075924
+ 496119.037002352 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496119.037002703 DEV_SLEEP_TIME dev:8 wake:000496119.103662693
+ 496119.037003665 SLEEP sleep:000000000.021079897 longest_wake:000158140
+ 496119.058114096 WAKE num_fds:0
+ 496119.058141173 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496119.058170250 FILL_AUDIO dev:8 hw_level:2192
+ 496119.058174380 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.058174856 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.058176079 WRITE_STREAMS_MIXED write_limit:0
+ 496119.058179060 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496119.058186803 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496119.058187274 DEV_SLEEP_TIME dev:8 wake:000496119.103846909
+ 496119.058189705 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496119.058271589 WAKE num_fds:1
+ 496119.058298411 FILL_AUDIO dev:8 hw_level:2192
+ 496119.058300596 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.058314814 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.058320702 DEV_STREAM_MIX written:1024 read:1024
+ 496119.058321610 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.058323068 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496119.058324672 STREAM_SLEEP_TIME id:140000 wake:000496119.079409257
+ 496119.058343765 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496119.058344301 DEV_SLEEP_TIME dev:8 wake:000496119.125323950
+ 496119.058345935 SLEEP sleep:000000000.021085307 longest_wake:000158140
+ 496119.079480188 WAKE num_fds:0
+ 496119.079495256 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496119.079509743 FILL_AUDIO dev:8 hw_level:2208
+ 496119.079512120 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.079512360 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.079512974 WRITE_STREAMS_MIXED write_limit:0
+ 496119.079514674 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496119.079521296 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496119.079521544 DEV_SLEEP_TIME dev:8 wake:000496119.125515340
+ 496119.079522548 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496119.079554700 WAKE num_fds:1
+ 496119.079570214 FILL_AUDIO dev:8 hw_level:2208
+ 496119.079570828 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.079578731 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.079580760 DEV_STREAM_MIX written:1024 read:1024
+ 496119.079581043 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.079581535 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496119.079582051 STREAM_SLEEP_TIME id:140000 wake:000496119.100742590
+ 496119.079586774 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496119.079586940 DEV_SLEEP_TIME dev:8 wake:000496119.146915148
+ 496119.079587422 SLEEP sleep:000000000.021160775 longest_wake:000158140
+ 496119.100983708 WAKE num_fds:0
+ 496119.101006952 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496119.101039086 FILL_AUDIO dev:8 hw_level:2176
+ 496119.101043727 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.101044840 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.101045897 WRITE_STREAMS_MIXED write_limit:0
+ 496119.101049455 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496119.101057769 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496119.101058316 DEV_SLEEP_TIME dev:8 wake:000496119.146384086
+ 496119.101060616 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496119.101152679 WAKE num_fds:1
+ 496119.101184647 FILL_AUDIO dev:8 hw_level:2176
+ 496119.101186787 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.101201316 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.101205696 DEV_STREAM_MIX written:1024 read:1024
+ 496119.101206237 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.101207300 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496119.101208487 STREAM_SLEEP_TIME id:140000 wake:000496119.122075923
+ 496119.101214476 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496119.101214857 DEV_SLEEP_TIME dev:8 wake:000496119.167874687
+ 496119.101215909 SLEEP sleep:000000000.020867902 longest_wake:000158140
+ 496119.122173783 WAKE num_fds:0
+ 496119.122197212 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496119.122228920 FILL_AUDIO dev:8 hw_level:2192
+ 496119.122234067 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.122234538 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.122235896 WRITE_STREAMS_MIXED write_limit:0
+ 496119.122238828 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496119.122248681 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496119.122249172 DEV_SLEEP_TIME dev:8 wake:000496119.167907429
+ 496119.122251026 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496119.122367113 WAKE num_fds:1
+ 496119.122402269 FILL_AUDIO dev:8 hw_level:2192
+ 496119.122404324 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.122419379 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.122424220 DEV_STREAM_MIX written:1024 read:1024
+ 496119.122424812 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.122425909 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496119.122427007 STREAM_SLEEP_TIME id:140000 wake:000496119.143409256
+ 496119.122433276 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496119.122433662 DEV_SLEEP_TIME dev:8 wake:000496119.189426520
+ 496119.122434704 SLEEP sleep:000000000.020982736 longest_wake:000158140
+ 496119.143482275 WAKE num_fds:0
+ 496119.143518679 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496119.143547377 FILL_AUDIO dev:8 hw_level:2208
+ 496119.143553840 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.143554353 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.143555696 WRITE_STREAMS_MIXED write_limit:0
+ 496119.143561538 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496119.143570165 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496119.143570707 DEV_SLEEP_TIME dev:8 wake:000496119.189563077
+ 496119.143572956 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496119.143667142 WAKE num_fds:1
+ 496119.143695684 FILL_AUDIO dev:8 hw_level:2208
+ 496119.143698005 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.143716969 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.143722870 DEV_STREAM_MIX written:1024 read:1024
+ 496119.143723459 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.143724850 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496119.143725889 STREAM_SLEEP_TIME id:140000 wake:000496119.164742589
+ 496119.143732011 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496119.143732364 DEV_SLEEP_TIME dev:8 wake:000496119.211058825
+ 496119.143734365 SLEEP sleep:000000000.021017097 longest_wake:000158140
+ 496119.164992925 WAKE num_fds:0
+ 496119.165016424 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496119.165048107 FILL_AUDIO dev:8 hw_level:2176
+ 496119.165052342 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.165053244 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.165054607 WRITE_STREAMS_MIXED write_limit:0
+ 496119.165057874 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496119.165065933 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496119.165066519 DEV_SLEEP_TIME dev:8 wake:000496119.210392460
+ 496119.165068399 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496119.165163333 WAKE num_fds:1
+ 496119.165194605 FILL_AUDIO dev:8 hw_level:2176
+ 496119.165196530 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.165211549 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.165216014 DEV_STREAM_MIX written:1024 read:1024
+ 496119.165216566 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.165217433 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496119.165218575 STREAM_SLEEP_TIME id:140000 wake:000496119.186075922
+ 496119.165224735 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496119.165225080 DEV_SLEEP_TIME dev:8 wake:000496119.231884610
+ 496119.165226073 SLEEP sleep:000000000.020857978 longest_wake:000158140
+ 496119.186110447 WAKE num_fds:0
+ 496119.186133796 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496119.186157525 FILL_AUDIO dev:8 hw_level:2192
+ 496119.186161725 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.186162151 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.186163629 WRITE_STREAMS_MIXED write_limit:0
+ 496119.186166561 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496119.186175261 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496119.186175828 DEV_SLEEP_TIME dev:8 wake:000496119.231834500
+ 496119.186177707 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496119.186268025 WAKE num_fds:1
+ 496119.186295584 FILL_AUDIO dev:8 hw_level:2192
+ 496119.186297734 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.186308379 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.186312207 DEV_STREAM_MIX written:1024 read:1024
+ 496119.186312824 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.186313921 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496119.186315004 STREAM_SLEEP_TIME id:140000 wake:000496119.207409255
+ 496119.186320902 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496119.186321253 DEV_SLEEP_TIME dev:8 wake:000496119.253314533
+ 496119.186322296 SLEEP sleep:000000000.021094722 longest_wake:000158140
+ 496119.207471590 WAKE num_fds:0
+ 496119.207496974 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496119.207525795 FILL_AUDIO dev:8 hw_level:2208
+ 496119.207530271 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.207530767 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.207532561 WRITE_STREAMS_MIXED write_limit:0
+ 496119.207535513 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496119.207543070 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496119.207543581 DEV_SLEEP_TIME dev:8 wake:000496119.253536665
+ 496119.207545335 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496119.207627590 WAKE num_fds:1
+ 496119.207655034 FILL_AUDIO dev:8 hw_level:2208
+ 496119.207657244 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.207673451 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.207679410 DEV_STREAM_MIX written:1024 read:1024
+ 496119.207680162 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.207681600 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496119.207683068 STREAM_SLEEP_TIME id:140000 wake:000496119.228742588
+ 496119.207690606 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496119.207691172 DEV_SLEEP_TIME dev:8 wake:000496119.275015700
+ 496119.207692600 SLEEP sleep:000000000.021060221 longest_wake:000158140
+ 496119.228853062 WAKE num_fds:0
+ 496119.228876095 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496119.228907021 FILL_AUDIO dev:8 hw_level:2176
+ 496119.228912308 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.228913446 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.228914463 WRITE_STREAMS_MIXED write_limit:0
+ 496119.228917315 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496119.228925719 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496119.228926215 DEV_SLEEP_TIME dev:8 wake:000496119.274252016
+ 496119.228928676 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496119.229018133 WAKE num_fds:1
+ 496119.229047681 FILL_AUDIO dev:8 hw_level:2176
+ 496119.229049234 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.229064119 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.229070388 DEV_STREAM_MIX written:1024 read:1024
+ 496119.229070959 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.229071967 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496119.229072949 STREAM_SLEEP_TIME id:140000 wake:000496119.250075921
+ 496119.229079509 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496119.229079870 DEV_SLEEP_TIME dev:8 wake:000496119.295739144
+ 496119.229080907 SLEEP sleep:000000000.021003443 longest_wake:000158140
+ 496119.250343383 WAKE num_fds:0
+ 496119.250367173 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496119.250399042 FILL_AUDIO dev:8 hw_level:2192
+ 496119.250403953 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.250404414 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.250406243 WRITE_STREAMS_MIXED write_limit:0
+ 496119.250409240 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496119.250418672 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496119.250419183 DEV_SLEEP_TIME dev:8 wake:000496119.296078016
+ 496119.250421268 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496119.250517861 WAKE num_fds:1
+ 496119.250549253 FILL_AUDIO dev:8 hw_level:2192
+ 496119.250551022 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.250566493 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.250571199 DEV_STREAM_MIX written:1024 read:1024
+ 496119.250571770 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.250572933 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496119.250573955 STREAM_SLEEP_TIME id:140000 wake:000496119.271409254
+ 496119.250580571 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496119.250580951 DEV_SLEEP_TIME dev:8 wake:000496119.317573489
+ 496119.250582019 SLEEP sleep:000000000.020835765 longest_wake:000158140
+ 496119.271663162 WAKE num_fds:0
+ 496119.271686306 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496119.271718400 FILL_AUDIO dev:8 hw_level:2208
+ 496119.271722684 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.271723126 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.271725130 WRITE_STREAMS_MIXED write_limit:0
+ 496119.271728042 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496119.271736516 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496119.271737058 DEV_SLEEP_TIME dev:8 wake:000496119.317729726
+ 496119.271738827 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496119.271834628 WAKE num_fds:1
+ 496119.271865213 FILL_AUDIO dev:8 hw_level:2160
+ 496119.271867238 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.271881546 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.271887019 DEV_STREAM_MIX written:1024 read:1024
+ 496119.271887685 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.271888612 FILL_AUDIO_DONE hw_level:2160 total_written:1024 min_cb_level:1024
+ 496119.271889700 STREAM_SLEEP_TIME id:140000 wake:000496119.292742587
+ 496119.271895884 SET_DEV_WAKE dev:8 hw_level:3184 sleep:3184
+ 496119.271896255 DEV_SLEEP_TIME dev:8 wake:000496119.338222557
+ 496119.271897242 SLEEP sleep:000000000.020853363 longest_wake:000158140
+ 496119.292999314 WAKE num_fds:0
+ 496119.293023229 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496119.293055559 FILL_AUDIO dev:8 hw_level:2176
+ 496119.293060059 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.293060520 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.293062169 WRITE_STREAMS_MIXED write_limit:0
+ 496119.293065406 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496119.293073836 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496119.293074387 DEV_SLEEP_TIME dev:8 wake:000496119.338400093
+ 496119.293076161 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496119.293169717 WAKE num_fds:1
+ 496119.293197421 FILL_AUDIO dev:8 hw_level:2176
+ 496119.293199205 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.293213278 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.293217447 DEV_STREAM_MIX written:1024 read:1024
+ 496119.293218024 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.293219562 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496119.293220524 STREAM_SLEEP_TIME id:140000 wake:000496119.314075920
+ 496119.293226634 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496119.293227014 DEV_SLEEP_TIME dev:8 wake:000496119.359886734
+ 496119.293228157 SLEEP sleep:000000000.020855852 longest_wake:000158140
+ 496119.314326940 WAKE num_fds:0
+ 496119.314367935 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496119.314398355 FILL_AUDIO dev:8 hw_level:2192
+ 496119.314402439 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.314402945 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.314404148 WRITE_STREAMS_MIXED write_limit:0
+ 496119.314406874 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496119.314415835 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496119.314416366 DEV_SLEEP_TIME dev:8 wake:000496119.360075290
+ 496119.314418211 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496119.314513646 WAKE num_fds:1
+ 496119.314545354 FILL_AUDIO dev:8 hw_level:2192
+ 496119.314547228 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.314562363 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.314566809 DEV_STREAM_MIX written:1024 read:1024
+ 496119.314567295 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.314568227 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496119.314569314 STREAM_SLEEP_TIME id:140000 wake:000496119.335409253
+ 496119.314575188 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496119.314575539 DEV_SLEEP_TIME dev:8 wake:000496119.381568818
+ 496119.314576531 SLEEP sleep:000000000.020840435 longest_wake:000158140
+ 496119.335516443 WAKE num_fds:0
+ 496119.335539261 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496119.335569801 FILL_AUDIO dev:8 hw_level:2208
+ 496119.335574597 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.335575099 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.335576286 WRITE_STREAMS_MIXED write_limit:0
+ 496119.335579890 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496119.335588279 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496119.335588815 DEV_SLEEP_TIME dev:8 wake:000496119.381581313
+ 496119.335590930 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496119.335683594 WAKE num_fds:1
+ 496119.335716806 FILL_AUDIO dev:8 hw_level:2208
+ 496119.335718695 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.335733775 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.335738676 DEV_STREAM_MIX written:1024 read:1024
+ 496119.335739258 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.335740340 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496119.335741733 STREAM_SLEEP_TIME id:140000 wake:000496119.356742586
+ 496119.335747988 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496119.335748384 DEV_SLEEP_TIME dev:8 wake:000496119.403074355
+ 496119.335749381 SLEEP sleep:000000000.021001564 longest_wake:000158140
+ 496119.356772896 WAKE num_fds:0
+ 496119.356796024 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496119.356818947 FILL_AUDIO dev:8 hw_level:2224
+ 496119.356823142 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.356823573 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.356824741 WRITE_STREAMS_MIXED write_limit:0
+ 496119.356827893 FILL_AUDIO_DONE hw_level:2224 total_written:0 min_cb_level:1024
+ 496119.356836297 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496119.356836784 DEV_SLEEP_TIME dev:8 wake:000496119.402162539
+ 496119.356838492 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496119.356924451 WAKE num_fds:1
+ 496119.356950401 FILL_AUDIO dev:8 hw_level:2176
+ 496119.356952285 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.356965009 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.356969244 DEV_STREAM_MIX written:1024 read:1024
+ 496119.356969806 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.356971003 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496119.356972096 STREAM_SLEEP_TIME id:140000 wake:000496119.378075919
+ 496119.356977999 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496119.356978415 DEV_SLEEP_TIME dev:8 wake:000496119.423638251
+ 496119.356979448 SLEEP sleep:000000000.021104334 longest_wake:000158140
+ 496119.378141728 WAKE num_fds:0
+ 496119.378163549 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496119.378184522 FILL_AUDIO dev:8 hw_level:2192
+ 496119.378189328 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.378189804 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.378190987 WRITE_STREAMS_MIXED write_limit:0
+ 496119.378193969 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496119.378202564 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496119.378203050 DEV_SLEEP_TIME dev:8 wake:000496119.423862198
+ 496119.378204924 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496119.378286091 WAKE num_fds:1
+ 496119.378312282 FILL_AUDIO dev:8 hw_level:2192
+ 496119.378313760 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.378325999 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.378342576 DEV_STREAM_MIX written:1024 read:1024
+ 496119.378343172 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.378344185 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496119.378345337 STREAM_SLEEP_TIME id:140000 wake:000496119.399409252
+ 496119.378351411 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496119.378351822 DEV_SLEEP_TIME dev:8 wake:000496119.445344856
+ 496119.378352865 SLEEP sleep:000000000.021064396 longest_wake:000158140
+ 496119.399442392 WAKE num_fds:0
+ 496119.399463837 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496119.399490884 FILL_AUDIO dev:8 hw_level:2208
+ 496119.399495094 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.399495550 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.399496602 WRITE_STREAMS_MIXED write_limit:0
+ 496119.399499955 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496119.399507778 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496119.399508294 DEV_SLEEP_TIME dev:8 wake:000496119.445501148
+ 496119.399510033 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496119.399592408 WAKE num_fds:1
+ 496119.399619716 FILL_AUDIO dev:8 hw_level:2208
+ 496119.399621180 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.399633964 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.399637964 DEV_STREAM_MIX written:1024 read:1024
+ 496119.399638485 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.399639652 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496119.399640655 STREAM_SLEEP_TIME id:140000 wake:000496119.420742585
+ 496119.399646398 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496119.399646749 DEV_SLEEP_TIME dev:8 wake:000496119.466973537
+ 496119.399647736 SLEEP sleep:000000000.021102381 longest_wake:000158140
+ 496119.420990443 WAKE num_fds:0
+ 496119.421014493 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496119.421045224 FILL_AUDIO dev:8 hw_level:2176
+ 496119.421050482 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.421050928 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.421052376 WRITE_STREAMS_MIXED write_limit:0
+ 496119.421055273 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496119.421063802 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496119.421064384 DEV_SLEEP_TIME dev:8 wake:000496119.466390335
+ 496119.421066238 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496119.421162485 WAKE num_fds:1
+ 496119.421190410 FILL_AUDIO dev:8 hw_level:2176
+ 496119.421192264 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.421208496 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.421213172 DEV_STREAM_MIX written:1024 read:1024
+ 496119.421213728 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.421214706 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496119.421215848 STREAM_SLEEP_TIME id:140000 wake:000496119.442075918
+ 496119.421222303 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496119.421222684 DEV_SLEEP_TIME dev:8 wake:000496119.487882013
+ 496119.421223726 SLEEP sleep:000000000.020860571 longest_wake:000158140
+ 496119.442117753 WAKE num_fds:0
+ 496119.442143662 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496119.442172815 FILL_AUDIO dev:8 hw_level:2192
+ 496119.442177621 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.442178072 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.442178974 WRITE_STREAMS_MIXED write_limit:0
+ 496119.442181911 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496119.442190205 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496119.442190776 DEV_SLEEP_TIME dev:8 wake:000496119.487849729
+ 496119.442192661 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496119.442277682 WAKE num_fds:1
+ 496119.442304804 FILL_AUDIO dev:8 hw_level:2192
+ 496119.442307466 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.442319594 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.442325988 DEV_STREAM_MIX written:1024 read:1024
+ 496119.442326830 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.442342225 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496119.442343733 STREAM_SLEEP_TIME id:140000 wake:000496119.463409251
+ 496119.442351025 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496119.442351657 DEV_SLEEP_TIME dev:8 wake:000496119.509343087
+ 496119.442353215 SLEEP sleep:000000000.021066164 longest_wake:000158140
+ 496119.463441223 WAKE num_fds:0
+ 496119.463463406 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496119.463494930 FILL_AUDIO dev:8 hw_level:2208
+ 496119.463499425 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.463499838 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.463501037 WRITE_STREAMS_MIXED write_limit:0
+ 496119.463504016 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496119.463513001 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496119.463513429 DEV_SLEEP_TIME dev:8 wake:000496119.509505367
+ 496119.463515069 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496119.463624173 WAKE num_fds:1
+ 496119.463650301 FILL_AUDIO dev:8 hw_level:2208
+ 496119.463653032 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.463670801 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.463678575 DEV_STREAM_MIX written:1024 read:1024
+ 496119.463679252 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.463680808 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496119.463682111 STREAM_SLEEP_TIME id:140000 wake:000496119.484742584
+ 496119.463688870 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496119.463689315 DEV_SLEEP_TIME dev:8 wake:000496119.531014955
+ 496119.463690759 SLEEP sleep:000000000.021060962 longest_wake:000158140
+ 496119.484761936 WAKE num_fds:0
+ 496119.484774842 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496119.484786984 FILL_AUDIO dev:8 hw_level:2224
+ 496119.484789594 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.484789778 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.484790606 WRITE_STREAMS_MIXED write_limit:0
+ 496119.484791910 FILL_AUDIO_DONE hw_level:2224 total_written:0 min_cb_level:1024
+ 496119.484797359 SET_DEV_WAKE dev:8 hw_level:2224 sleep:2224
+ 496119.484797534 DEV_SLEEP_TIME dev:8 wake:000496119.531125750
+ 496119.484798276 SLEEP sleep:000000000.046333333 longest_wake:000158140
+ 496119.484828393 WAKE num_fds:1
+ 496119.484842892 FILL_AUDIO dev:8 hw_level:2176
+ 496119.484843484 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.484849786 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.484852445 DEV_STREAM_MIX written:1024 read:1024
+ 496119.484852760 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.484853392 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496119.484853881 STREAM_SLEEP_TIME id:140000 wake:000496119.506075917
+ 496119.484858408 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496119.484858539 DEV_SLEEP_TIME dev:8 wake:000496119.551520364
+ 496119.484858931 SLEEP sleep:000000000.021222219 longest_wake:000158140
+ 496119.506277376 WAKE num_fds:0
+ 496119.506291073 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496119.506307673 FILL_AUDIO dev:8 hw_level:2192
+ 496119.506310095 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.506310261 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.506310695 WRITE_STREAMS_MIXED write_limit:0
+ 496119.506312020 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496119.506317760 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496119.506317950 DEV_SLEEP_TIME dev:8 wake:000496119.551979289
+ 496119.506318606 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496119.506364111 WAKE num_fds:1
+ 496119.506379211 FILL_AUDIO dev:8 hw_level:2192
+ 496119.506380031 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.506386971 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.506388635 DEV_STREAM_MIX written:1024 read:1024
+ 496119.506388839 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.506389202 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496119.506389581 STREAM_SLEEP_TIME id:140000 wake:000496119.527409250
+ 496119.506394062 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496119.506394200 DEV_SLEEP_TIME dev:8 wake:000496119.573389409
+ 496119.506394609 SLEEP sleep:000000000.021019841 longest_wake:000158140
+ 496119.527434443 WAKE num_fds:0
+ 496119.527460343 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496119.527484093 FILL_AUDIO dev:8 hw_level:2208
+ 496119.527489130 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.527489626 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.527490718 WRITE_STREAMS_MIXED write_limit:0
+ 496119.527495374 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496119.527504154 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496119.527504685 DEV_SLEEP_TIME dev:8 wake:000496119.573496602
+ 496119.527508008 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496119.527588594 WAKE num_fds:1
+ 496119.527616604 FILL_AUDIO dev:8 hw_level:2208
+ 496119.527619475 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.527633914 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.527640614 DEV_STREAM_MIX written:1024 read:1024
+ 496119.527641321 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.527643035 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496119.527644568 STREAM_SLEEP_TIME id:140000 wake:000496119.548742583
+ 496119.527651885 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496119.527653088 DEV_SLEEP_TIME dev:8 wake:000496119.594977240
+ 496119.527654657 SLEEP sleep:000000000.021098676 longest_wake:000158140
+ 496119.548999664 WAKE num_fds:0
+ 496119.549022737 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496119.549054295 FILL_AUDIO dev:8 hw_level:2176
+ 496119.549059261 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.549059843 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.549061186 WRITE_STREAMS_MIXED write_limit:0
+ 496119.549064483 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496119.549072813 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496119.549073359 DEV_SLEEP_TIME dev:8 wake:000496119.594399250
+ 496119.549075689 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496119.549176036 WAKE num_fds:1
+ 496119.549203574 FILL_AUDIO dev:8 hw_level:2176
+ 496119.549205544 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.549220529 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.549224718 DEV_STREAM_MIX written:1024 read:1024
+ 496119.549225270 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.549226192 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496119.549227244 STREAM_SLEEP_TIME id:140000 wake:000496119.570075916
+ 496119.549234205 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496119.549234591 DEV_SLEEP_TIME dev:8 wake:000496119.615893439
+ 496119.549235593 SLEEP sleep:000000000.020849143 longest_wake:000158140
+ 496119.570344229 WAKE num_fds:0
+ 496119.570367668 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496119.570397667 FILL_AUDIO dev:8 hw_level:2192
+ 496119.570402293 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.570402749 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.570403942 WRITE_STREAMS_MIXED write_limit:0
+ 496119.570406964 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496119.570415654 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496119.570416160 DEV_SLEEP_TIME dev:8 wake:000496119.616075023
+ 496119.570417854 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496119.570517549 WAKE num_fds:1
+ 496119.570548375 FILL_AUDIO dev:8 hw_level:2192
+ 496119.570550235 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.570565405 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.570570125 DEV_STREAM_MIX written:1024 read:1024
+ 496119.570570832 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.570571614 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496119.570572651 STREAM_SLEEP_TIME id:140000 wake:000496119.591409249
+ 496119.570578309 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496119.570578660 DEV_SLEEP_TIME dev:8 wake:000496119.637572175
+ 496119.570579662 SLEEP sleep:000000000.020837074 longest_wake:000158140
+ 496119.591447839 WAKE num_fds:0
+ 496119.591471083 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496119.591498381 FILL_AUDIO dev:8 hw_level:2208
+ 496119.591502641 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.591503082 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.591504470 WRITE_STREAMS_MIXED write_limit:0
+ 496119.591507331 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496119.591515575 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496119.591516077 DEV_SLEEP_TIME dev:8 wake:000496119.637508639
+ 496119.591517911 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496119.591605508 WAKE num_fds:1
+ 496119.591633272 FILL_AUDIO dev:8 hw_level:2208
+ 496119.591634976 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.591649530 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.591655148 DEV_STREAM_MIX written:1024 read:1024
+ 496119.591655869 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.591657373 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496119.591658806 STREAM_SLEEP_TIME id:140000 wake:000496119.612742582
+ 496119.591665451 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496119.591665907 DEV_SLEEP_TIME dev:8 wake:000496119.658991447
+ 496119.591667235 SLEEP sleep:000000000.021084468 longest_wake:000158140
+ 496119.612826659 WAKE num_fds:0
+ 496119.612848891 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496119.612873648 FILL_AUDIO dev:8 hw_level:2176
+ 496119.612878048 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.612878489 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.612879697 WRITE_STREAMS_MIXED write_limit:0
+ 496119.612882694 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496119.612890742 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496119.612891319 DEV_SLEEP_TIME dev:8 wake:000496119.658217229
+ 496119.612893073 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496119.612975583 WAKE num_fds:1
+ 496119.613003011 FILL_AUDIO dev:8 hw_level:2176
+ 496119.613004364 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.613018011 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.613022115 DEV_STREAM_MIX written:1024 read:1024
+ 496119.613022672 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.613023754 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496119.613024812 STREAM_SLEEP_TIME id:140000 wake:000496119.634075915
+ 496119.613030515 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496119.613030896 DEV_SLEEP_TIME dev:8 wake:000496119.679690987
+ 496119.613031868 SLEEP sleep:000000000.021051594 longest_wake:000158140
+ 496119.634323952 WAKE num_fds:0
+ 496119.634365252 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496119.634397427 FILL_AUDIO dev:8 hw_level:2192
+ 496119.634401792 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.634402208 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.634403330 WRITE_STREAMS_MIXED write_limit:0
+ 496119.634406733 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496119.634415082 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496119.634415644 DEV_SLEEP_TIME dev:8 wake:000496119.680074867
+ 496119.634417673 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496119.634513685 WAKE num_fds:1
+ 496119.634542587 FILL_AUDIO dev:8 hw_level:2192
+ 496119.634544110 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.634559245 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.634563565 DEV_STREAM_MIX written:1024 read:1024
+ 496119.634564222 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.634565304 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496119.634566527 STREAM_SLEEP_TIME id:140000 wake:000496119.655409248
+ 496119.634572972 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496119.634573353 DEV_SLEEP_TIME dev:8 wake:000496119.701566016
+ 496119.634574335 SLEEP sleep:000000000.020843232 longest_wake:000158140
+ 496119.655663146 WAKE num_fds:0
+ 496119.655686319 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496119.655718418 FILL_AUDIO dev:8 hw_level:2208
+ 496119.655723791 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.655724302 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.655725710 WRITE_STREAMS_MIXED write_limit:0
+ 496119.655728542 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496119.655736746 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496119.655737322 DEV_SLEEP_TIME dev:8 wake:000496119.701729780
+ 496119.655739201 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496119.655836651 WAKE num_fds:1
+ 496119.655867467 FILL_AUDIO dev:8 hw_level:2160
+ 496119.655869332 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.655883900 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.655888255 DEV_STREAM_MIX written:1024 read:1024
+ 496119.655888807 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.655890465 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:0
+ 496119.655891047 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.655891503 WRITE_STREAMS_MIXED write_limit:0
+ 496119.655892059 FILL_AUDIO_DONE hw_level:2160 total_written:1024 min_cb_level:1024
+ 496119.655893267 STREAM_SLEEP_TIME id:140000 wake:000496119.676742581
+ 496119.655899216 SET_DEV_WAKE dev:8 hw_level:3184 sleep:3184
+ 496119.655899561 DEV_SLEEP_TIME dev:8 wake:000496119.722225999
+ 496119.655900579 SLEEP sleep:000000000.020849915 longest_wake:000158140
+ 496119.676782360 WAKE num_fds:0
+ 496119.676802491 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496119.676828715 FILL_AUDIO dev:8 hw_level:2176
+ 496119.676831930 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.676832205 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.676832933 WRITE_STREAMS_MIXED write_limit:0
+ 496119.676835095 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496119.676841542 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496119.676841833 DEV_SLEEP_TIME dev:8 wake:000496119.722169273
+ 496119.676843191 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496119.676901138 WAKE num_fds:1
+ 496119.676923101 FILL_AUDIO dev:8 hw_level:2176
+ 496119.676924667 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.676927011 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.676930681 DEV_STREAM_MIX written:1024 read:1024
+ 496119.676931111 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.676932162 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496119.676933078 STREAM_SLEEP_TIME id:140000 wake:000496119.698075914
+ 496119.676939028 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496119.676939378 DEV_SLEEP_TIME dev:8 wake:000496119.743599341
+ 496119.676940404 SLEEP sleep:000000000.021143239 longest_wake:000158140
+ 496119.698136425 WAKE num_fds:0
+ 496119.698158456 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496119.698186947 FILL_AUDIO dev:8 hw_level:2192
+ 496119.698191006 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.698191492 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.698192615 WRITE_STREAMS_MIXED write_limit:0
+ 496119.698195446 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496119.698203269 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496119.698203800 DEV_SLEEP_TIME dev:8 wake:000496119.743863350
+ 496119.698205620 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496119.698283585 WAKE num_fds:1
+ 496119.698308688 FILL_AUDIO dev:8 hw_level:2192
+ 496119.698310517 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.698312035 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.698315914 DEV_STREAM_MIX written:1024 read:1024
+ 496119.698316430 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.698317528 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496119.698318540 STREAM_SLEEP_TIME id:140000 wake:000496119.719409247
+ 496119.698324143 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496119.698324524 DEV_SLEEP_TIME dev:8 wake:000496119.765318074
+ 496119.698325461 SLEEP sleep:000000000.021091173 longest_wake:000158140
+ 496119.719658229 WAKE num_fds:0
+ 496119.719682220 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496119.719712981 FILL_AUDIO dev:8 hw_level:2208
+ 496119.719717286 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.719717742 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.719718839 WRITE_STREAMS_MIXED write_limit:0
+ 496119.719721741 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496119.719730717 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496119.719731308 DEV_SLEEP_TIME dev:8 wake:000496119.765723665
+ 496119.719733202 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496119.719836902 WAKE num_fds:1
+ 496119.719868354 FILL_AUDIO dev:8 hw_level:2160
+ 496119.719869998 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.719871687 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.719876724 DEV_STREAM_MIX written:1024 read:1024
+ 496119.719877250 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.719878513 FILL_AUDIO_DONE hw_level:2160 total_written:1024 min_cb_level:1024
+ 496119.719880016 STREAM_SLEEP_TIME id:140000 wake:000496119.740742580
+ 496119.719885724 SET_DEV_WAKE dev:8 hw_level:3184 sleep:3184
+ 496119.719886100 DEV_SLEEP_TIME dev:8 wake:000496119.786212602
+ 496119.719887067 SLEEP sleep:000000000.020863311 longest_wake:000158140
+ 496119.740777485 WAKE num_fds:0
+ 496119.740799787 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496119.740819222 FILL_AUDIO dev:8 hw_level:2224
+ 496119.740824434 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.740824920 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.740826088 WRITE_STREAMS_MIXED write_limit:0
+ 496119.740828989 FILL_AUDIO_DONE hw_level:2224 total_written:0 min_cb_level:1024
+ 496119.740837805 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496119.740838286 DEV_SLEEP_TIME dev:8 wake:000496119.786163525
+ 496119.740840075 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496119.740937109 WAKE num_fds:1
+ 496119.740963259 FILL_AUDIO dev:8 hw_level:2176
+ 496119.740965564 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.740968697 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.740973979 DEV_STREAM_MIX written:1024 read:1024
+ 496119.740974670 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.740975984 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496119.740977372 STREAM_SLEEP_TIME id:140000 wake:000496119.762075913
+ 496119.740983656 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496119.740984132 DEV_SLEEP_TIME dev:8 wake:000496119.807643386
+ 496119.740985510 SLEEP sleep:000000000.021099193 longest_wake:000158140
+ 496119.762184771 WAKE num_fds:0
+ 496119.762207604 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496119.762239753 FILL_AUDIO dev:8 hw_level:2192
+ 496119.762244123 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.762245146 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.762246414 WRITE_STREAMS_MIXED write_limit:0
+ 496119.762249320 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496119.762257569 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496119.762258131 DEV_SLEEP_TIME dev:8 wake:000496119.807917415
+ 496119.762261579 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496119.762376087 WAKE num_fds:1
+ 496119.762404828 FILL_AUDIO dev:8 hw_level:2192
+ 496119.762407088 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.762408577 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.762412676 DEV_STREAM_MIX written:1024 read:1024
+ 496119.762413358 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.762414671 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496119.762416124 STREAM_SLEEP_TIME id:140000 wake:000496119.783409246
+ 496119.762422289 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496119.762422689 DEV_SLEEP_TIME dev:8 wake:000496119.829415488
+ 496119.762423667 SLEEP sleep:000000000.020993758 longest_wake:000158140
+ 496119.783498916 WAKE num_fds:0
+ 496119.783520847 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496119.783546040 FILL_AUDIO dev:8 hw_level:2208
+ 496119.783550180 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.783550656 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.783551718 WRITE_STREAMS_MIXED write_limit:0
+ 496119.783554464 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496119.783562839 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496119.783563390 DEV_SLEEP_TIME dev:8 wake:000496119.829555798
+ 496119.783565164 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496119.783648807 WAKE num_fds:1
+ 496119.783676742 FILL_AUDIO dev:8 hw_level:2208
+ 496119.783678280 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.783680801 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.783684720 DEV_STREAM_MIX written:1024 read:1024
+ 496119.783685236 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.783686750 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496119.783687847 STREAM_SLEEP_TIME id:140000 wake:000496119.804742579
+ 496119.783694097 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496119.783694618 DEV_SLEEP_TIME dev:8 wake:000496119.851020649
+ 496119.783695881 SLEEP sleep:000000000.021055263 longest_wake:000158140
+ 496119.804846154 WAKE num_fds:0
+ 496119.804869563 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496119.804900975 FILL_AUDIO dev:8 hw_level:2176
+ 496119.804905260 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.804906282 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.804907335 WRITE_STREAMS_MIXED write_limit:0
+ 496119.804911094 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496119.804919623 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496119.804920190 DEV_SLEEP_TIME dev:8 wake:000496119.850245890
+ 496119.804922224 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496119.805017163 WAKE num_fds:1
+ 496119.805048155 FILL_AUDIO dev:8 hw_level:2176
+ 496119.805049984 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.805051578 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.805055662 DEV_STREAM_MIX written:1024 read:1024
+ 496119.805056218 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.805057331 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496119.805058439 STREAM_SLEEP_TIME id:140000 wake:000496119.826075912
+ 496119.805064147 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496119.805064523 DEV_SLEEP_TIME dev:8 wake:000496119.871724664
+ 496119.805065520 SLEEP sleep:000000000.021017914 longest_wake:000158140
+ 496119.826181499 WAKE num_fds:0
+ 496119.826204557 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496119.826235859 FILL_AUDIO dev:8 hw_level:2192
+ 496119.826240500 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.826240931 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.826242559 WRITE_STREAMS_MIXED write_limit:0
+ 496119.826245521 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496119.826253670 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496119.826254236 DEV_SLEEP_TIME dev:8 wake:000496119.871913500
+ 496119.826256056 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496119.826373791 WAKE num_fds:1
+ 496119.826402197 FILL_AUDIO dev:8 hw_level:2192
+ 496119.826404943 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.826406793 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.826410982 DEV_STREAM_MIX written:1024 read:1024
+ 496119.826411508 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.826412541 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496119.826413729 STREAM_SLEEP_TIME id:140000 wake:000496119.847409245
+ 496119.826419271 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496119.826419938 DEV_SLEEP_TIME dev:8 wake:000496119.893413262
+ 496119.826421141 SLEEP sleep:000000000.020995983 longest_wake:000158140
+ 496119.847573032 WAKE num_fds:0
+ 496119.847597188 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496119.847631537 FILL_AUDIO dev:8 hw_level:2208
+ 496119.847636143 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.847636594 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.847638057 WRITE_STREAMS_MIXED write_limit:0
+ 496119.847642778 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496119.847652751 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496119.847653332 DEV_SLEEP_TIME dev:8 wake:000496119.893644703
+ 496119.847655563 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496119.847751424 WAKE num_fds:1
+ 496119.847777820 FILL_AUDIO dev:8 hw_level:2208
+ 496119.847779419 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.847780937 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.847786791 DEV_STREAM_MIX written:1024 read:1024
+ 496119.847787337 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.847788499 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496119.847789517 STREAM_SLEEP_TIME id:140000 wake:000496119.868742578
+ 496119.847794999 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496119.847795365 DEV_SLEEP_TIME dev:8 wake:000496119.915122379
+ 496119.847796523 SLEEP sleep:000000000.020953532 longest_wake:000158140
+ 496119.868841929 WAKE num_fds:0
+ 496119.868866084 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496119.868897878 FILL_AUDIO dev:8 hw_level:2176
+ 496119.868902263 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.868903210 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.868904428 WRITE_STREAMS_MIXED write_limit:0
+ 496119.868907149 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496119.868915248 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496119.868915794 DEV_SLEEP_TIME dev:8 wake:000496119.914241845
+ 496119.868917784 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496119.869015113 WAKE num_fds:1
+ 496119.869048987 FILL_AUDIO dev:8 hw_level:2176
+ 496119.869050751 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.869053281 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.869058158 DEV_STREAM_MIX written:1024 read:1024
+ 496119.869058894 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.869059566 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496119.869060543 STREAM_SLEEP_TIME id:140000 wake:000496119.890075911
+ 496119.869066547 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496119.869066923 DEV_SLEEP_TIME dev:8 wake:000496119.935726768
+ 496119.869068086 SLEEP sleep:000000000.021015809 longest_wake:000158140
+ 496119.890110720 WAKE num_fds:0
+ 496119.890133212 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496119.890157759 FILL_AUDIO dev:8 hw_level:2192
+ 496119.890162880 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.890163321 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.890164504 WRITE_STREAMS_MIXED write_limit:0
+ 496119.890167491 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496119.890175499 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496119.890176021 DEV_SLEEP_TIME dev:8 wake:000496119.935835435
+ 496119.890177800 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496119.890260912 WAKE num_fds:1
+ 496119.890286265 FILL_AUDIO dev:8 hw_level:2192
+ 496119.890288195 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.890290149 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.890295557 DEV_STREAM_MIX written:1024 read:1024
+ 496119.890296268 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.890297671 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496119.890298914 STREAM_SLEEP_TIME id:140000 wake:000496119.911409244
+ 496119.890305374 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496119.890305805 DEV_SLEEP_TIME dev:8 wake:000496119.957298358
+ 496119.890307223 SLEEP sleep:000000000.021110886 longest_wake:000158140
+ 496119.911457981 WAKE num_fds:0
+ 496119.911482252 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496119.911511630 FILL_AUDIO dev:8 hw_level:2208
+ 496119.911516807 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.911517404 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.911518641 WRITE_STREAMS_MIXED write_limit:0
+ 496119.911521668 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496119.911529396 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496119.911529912 DEV_SLEEP_TIME dev:8 wake:000496119.957522851
+ 496119.911531636 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496119.911612914 WAKE num_fds:1
+ 496119.911641044 FILL_AUDIO dev:8 hw_level:2208
+ 496119.911643039 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.911645043 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.911652420 DEV_STREAM_MIX written:1024 read:1024
+ 496119.911653147 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.911654886 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496119.911656239 STREAM_SLEEP_TIME id:140000 wake:000496119.932742577
+ 496119.911663180 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496119.911663601 DEV_SLEEP_TIME dev:8 wake:000496119.978989021
+ 496119.911665014 SLEEP sleep:000000000.021086889 longest_wake:000158140
+ 496119.932781594 WAKE num_fds:0
+ 496119.932810225 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496119.932837659 FILL_AUDIO dev:8 hw_level:2176
+ 496119.932842344 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.932842941 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.932844214 WRITE_STREAMS_MIXED write_limit:0
+ 496119.932847311 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496119.932856257 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496119.932856723 DEV_SLEEP_TIME dev:8 wake:000496119.978182037
+ 496119.932858742 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496119.932950314 WAKE num_fds:1
+ 496119.932981345 FILL_AUDIO dev:8 hw_level:2176
+ 496119.932983395 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.932985836 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.932990792 DEV_STREAM_MIX written:1024 read:1024
+ 496119.932991338 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.932992311 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496119.932993358 STREAM_SLEEP_TIME id:140000 wake:000496119.954075910
+ 496119.932999216 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496119.932999838 DEV_SLEEP_TIME dev:8 wake:000496119.999659568
+ 496119.933001116 SLEEP sleep:000000000.021083008 longest_wake:000158140
+ 496119.954146565 WAKE num_fds:0
+ 496119.954162536 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496119.954180693 FILL_AUDIO dev:8 hw_level:2192
+ 496119.954183341 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.954183605 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.954184349 WRITE_STREAMS_MIXED write_limit:0
+ 496119.954186514 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496119.954192771 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496119.954193049 DEV_SLEEP_TIME dev:8 wake:000496119.999853959
+ 496119.954194267 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496119.954240476 WAKE num_fds:1
+ 496119.954258950 FILL_AUDIO dev:8 hw_level:2192
+ 496119.954259846 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.954260998 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.954266147 DEV_STREAM_MIX written:1024 read:1024
+ 496119.954266591 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.954267353 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496119.954268157 STREAM_SLEEP_TIME id:140000 wake:000496119.975409243
+ 496119.954273532 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496119.954273850 DEV_SLEEP_TIME dev:8 wake:000496120.021267834
+ 496119.954274529 SLEEP sleep:000000000.021141409 longest_wake:000158140
+ 496119.975463624 WAKE num_fds:0
+ 496119.975512667 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496119.975552860 FILL_AUDIO dev:8 hw_level:2208
+ 496119.975562763 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.975563259 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.975564547 WRITE_STREAMS_MIXED write_limit:0
+ 496119.975572255 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496119.975582799 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496119.975583275 DEV_SLEEP_TIME dev:8 wake:000496120.021574064
+ 496119.975585826 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496119.975725950 WAKE num_fds:1
+ 496119.975762304 FILL_AUDIO dev:8 hw_level:2208
+ 496119.975765351 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.975769365 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.975777188 DEV_STREAM_MIX written:1024 read:1024
+ 496119.975777829 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.975779543 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496119.975781348 STREAM_SLEEP_TIME id:140000 wake:000496119.996742576
+ 496119.975787873 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496119.975788269 DEV_SLEEP_TIME dev:8 wake:000496120.043114219
+ 496119.975789411 SLEEP sleep:000000000.020961690 longest_wake:000158140
+ 496119.996809488 WAKE num_fds:0
+ 496119.996830326 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496119.996856422 FILL_AUDIO dev:8 hw_level:2176
+ 496119.996860426 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496119.996860867 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496119.996861919 WRITE_STREAMS_MIXED write_limit:0
+ 496119.996864570 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496119.996872348 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496119.996872845 DEV_SLEEP_TIME dev:8 wake:000496120.042199096
+ 496119.996874814 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496119.996972104 WAKE num_fds:1
+ 496119.996998314 FILL_AUDIO dev:8 hw_level:2176
+ 496119.996999908 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496119.997001361 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496119.997005220 DEV_STREAM_MIX written:1024 read:1024
+ 496119.997005761 WRITE_STREAMS_MIXED write_limit:1024
+ 496119.997006899 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496119.997007997 STREAM_SLEEP_TIME id:140000 wake:000496120.018075909
+ 496119.997013484 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496119.997013880 DEV_SLEEP_TIME dev:8 wake:000496120.063674186
+ 496119.997014872 SLEEP sleep:000000000.021068389 longest_wake:000158140
+ 496120.018176962 WAKE num_fds:0
+ 496120.018200617 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496120.018232290 FILL_AUDIO dev:8 hw_level:2192
+ 496120.018237242 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.018237748 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.018238900 WRITE_STREAMS_MIXED write_limit:0
+ 496120.018243346 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496120.018251489 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496120.018252036 DEV_SLEEP_TIME dev:8 wake:000496120.063911239
+ 496120.018254025 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496120.018371866 WAKE num_fds:1
+ 496120.018402292 FILL_AUDIO dev:8 hw_level:2192
+ 496120.018404587 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.018406206 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.018410290 DEV_STREAM_MIX written:1024 read:1024
+ 496120.018410856 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.018412074 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496120.018413157 STREAM_SLEEP_TIME id:140000 wake:000496120.039409242
+ 496120.018419020 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496120.018419371 DEV_SLEEP_TIME dev:8 wake:000496120.085412691
+ 496120.018420468 SLEEP sleep:000000000.020996551 longest_wake:000158140
+ 496120.039592015 WAKE num_fds:0
+ 496120.039614197 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496120.039645043 FILL_AUDIO dev:8 hw_level:2208
+ 496120.039649328 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.039649764 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.039651312 WRITE_STREAMS_MIXED write_limit:0
+ 496120.039654023 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496120.039662277 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496120.039662864 DEV_SLEEP_TIME dev:8 wake:000496120.085655417
+ 496120.039664573 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496120.039757006 WAKE num_fds:1
+ 496120.039786254 FILL_AUDIO dev:8 hw_level:2208
+ 496120.039788358 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.039789902 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.039794478 DEV_STREAM_MIX written:1024 read:1024
+ 496120.039795144 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.039796482 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496120.039797560 STREAM_SLEEP_TIME id:140000 wake:000496120.060742575
+ 496120.039803468 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496120.039803824 DEV_SLEEP_TIME dev:8 wake:000496120.107130392
+ 496120.039804832 SLEEP sleep:000000000.020945516 longest_wake:000158140
+ 496120.060797771 WAKE num_fds:0
+ 496120.060820273 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496120.060854302 FILL_AUDIO dev:8 hw_level:2176
+ 496120.060859644 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.060860160 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.060861328 WRITE_STREAMS_MIXED write_limit:0
+ 496120.060865207 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496120.060874082 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496120.060874618 DEV_SLEEP_TIME dev:8 wake:000496120.106199748
+ 496120.060876327 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496120.060978919 WAKE num_fds:1
+ 496120.061008252 FILL_AUDIO dev:8 hw_level:2176
+ 496120.061010392 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.061012447 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.061017007 DEV_STREAM_MIX written:1024 read:1024
+ 496120.061017714 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.061018736 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496120.061019864 STREAM_SLEEP_TIME id:140000 wake:000496120.082075908
+ 496120.061025281 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496120.061025662 DEV_SLEEP_TIME dev:8 wake:000496120.127686029
+ 496120.061026895 SLEEP sleep:000000000.021056545 longest_wake:000158140
+ 496120.082178195 WAKE num_fds:0
+ 496120.082200907 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496120.082232125 FILL_AUDIO dev:8 hw_level:2192
+ 496120.082237612 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.082238705 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.082240629 WRITE_STREAMS_MIXED write_limit:0
+ 496120.082243696 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496120.082252522 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496120.082253519 DEV_SLEEP_TIME dev:8 wake:000496120.127911745
+ 496120.082255629 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496120.082367060 WAKE num_fds:1
+ 496120.082395866 FILL_AUDIO dev:8 hw_level:2192
+ 496120.082397595 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.082399144 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.082405188 DEV_STREAM_MIX written:1024 read:1024
+ 496120.082405950 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.082407373 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496120.082408405 STREAM_SLEEP_TIME id:140000 wake:000496120.103409241
+ 496120.082413963 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496120.082414670 DEV_SLEEP_TIME dev:8 wake:000496120.149407924
+ 496120.082415858 SLEEP sleep:000000000.021001317 longest_wake:000158140
+ 496120.103453866 WAKE num_fds:0
+ 496120.103476263 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496120.103506513 FILL_AUDIO dev:8 hw_level:2208
+ 496120.103511449 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.103512000 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.103513504 WRITE_STREAMS_MIXED write_limit:0
+ 496120.103516381 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496120.103524870 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496120.103525436 DEV_SLEEP_TIME dev:8 wake:000496120.149517653
+ 496120.103527722 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496120.103621638 WAKE num_fds:1
+ 496120.103650109 FILL_AUDIO dev:8 hw_level:2208
+ 496120.103651953 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.103653607 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.103658398 DEV_STREAM_MIX written:1024 read:1024
+ 496120.103658960 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.103659942 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496120.103660949 STREAM_SLEEP_TIME id:140000 wake:000496120.124742574
+ 496120.103666838 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496120.103667234 DEV_SLEEP_TIME dev:8 wake:000496120.170993811
+ 496120.103668261 SLEEP sleep:000000000.021082096 longest_wake:000158140
+ 496120.124785237 WAKE num_fds:0
+ 496120.124806356 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496120.124831689 FILL_AUDIO dev:8 hw_level:2176
+ 496120.124835618 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.124836190 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.124837197 WRITE_STREAMS_MIXED write_limit:0
+ 496120.124840109 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496120.124848789 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496120.124849265 DEV_SLEEP_TIME dev:8 wake:000496120.170174865
+ 496120.124851064 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496120.124937794 WAKE num_fds:1
+ 496120.124963223 FILL_AUDIO dev:8 hw_level:2176
+ 496120.124964751 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.124966350 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.124970339 DEV_STREAM_MIX written:1024 read:1024
+ 496120.124970971 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.124972018 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496120.124973111 STREAM_SLEEP_TIME id:140000 wake:000496120.146075907
+ 496120.124978849 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496120.124979235 DEV_SLEEP_TIME dev:8 wake:000496120.191639361
+ 496120.124980252 SLEEP sleep:000000000.021103212 longest_wake:000158140
+ 496120.146169219 WAKE num_fds:0
+ 496120.146191606 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496120.146222547 FILL_AUDIO dev:8 hw_level:2192
+ 496120.146228346 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.146228812 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.146230265 WRITE_STREAMS_MIXED write_limit:0
+ 496120.146233297 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496120.146243125 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496120.146243676 DEV_SLEEP_TIME dev:8 wake:000496120.191901662
+ 496120.146245530 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496120.146362163 WAKE num_fds:1
+ 496120.146392519 FILL_AUDIO dev:8 hw_level:2192
+ 496120.146394378 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.146396062 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.146400091 DEV_STREAM_MIX written:1024 read:1024
+ 496120.146400963 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.146402020 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496120.146403308 STREAM_SLEEP_TIME id:140000 wake:000496120.167409240
+ 496120.146409392 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496120.146409743 DEV_SLEEP_TIME dev:8 wake:000496120.213402737
+ 496120.146410746 SLEEP sleep:000000000.021006503 longest_wake:000158140
+ 496120.167670100 WAKE num_fds:0
+ 496120.167693524 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496120.167724260 FILL_AUDIO dev:8 hw_level:2208
+ 496120.167728821 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.167729272 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.167730389 WRITE_STREAMS_MIXED write_limit:0
+ 496120.167733466 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496120.167742041 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496120.167742547 DEV_SLEEP_TIME dev:8 wake:000496120.213734845
+ 496120.167744286 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496120.167843866 WAKE num_fds:1
+ 496120.167874216 FILL_AUDIO dev:8 hw_level:2160
+ 496120.167876241 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.167878035 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.167882305 DEV_STREAM_MIX written:1024 read:1024
+ 496120.167882866 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.167883713 FILL_AUDIO_DONE hw_level:2160 total_written:1024 min_cb_level:1024
+ 496120.167884645 STREAM_SLEEP_TIME id:140000 wake:000496120.188742573
+ 496120.167890554 SET_DEV_WAKE dev:8 hw_level:3184 sleep:3184
+ 496120.167890915 DEV_SLEEP_TIME dev:8 wake:000496120.234217522
+ 496120.167892268 SLEEP sleep:000000000.020858384 longest_wake:000158140
+ 496120.188990921 WAKE num_fds:0
+ 496120.189014721 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496120.189046640 FILL_AUDIO dev:8 hw_level:2176
+ 496120.189050845 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.189051361 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.189052930 WRITE_STREAMS_MIXED write_limit:0
+ 496120.189055856 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496120.189064396 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496120.189064967 DEV_SLEEP_TIME dev:8 wake:000496120.234390863
+ 496120.189067603 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496120.189162332 WAKE num_fds:1
+ 496120.189191139 FILL_AUDIO dev:8 hw_level:2176
+ 496120.189193003 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.189194577 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.189199142 DEV_STREAM_MIX written:1024 read:1024
+ 496120.189199708 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.189200570 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496120.189201663 STREAM_SLEEP_TIME id:140000 wake:000496120.210075906
+ 496120.189207286 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496120.189207657 DEV_SLEEP_TIME dev:8 wake:000496120.255867863
+ 496120.189208654 SLEEP sleep:000000000.020874709 longest_wake:000158140
+ 496120.210165596 WAKE num_fds:0
+ 496120.210210895 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496120.210251669 FILL_AUDIO dev:8 hw_level:2192
+ 496120.210260850 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.210261337 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.210262830 WRITE_STREAMS_MIXED write_limit:0
+ 496120.210268693 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496120.210279498 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496120.210280035 DEV_SLEEP_TIME dev:8 wake:000496120.255938211
+ 496120.210282661 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496120.210445466 WAKE num_fds:1
+ 496120.210479965 FILL_AUDIO dev:8 hw_level:2192
+ 496120.210481915 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.210487217 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.210495035 DEV_STREAM_MIX written:1024 read:1024
+ 496120.210495737 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.210496979 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496120.210497957 STREAM_SLEEP_TIME id:140000 wake:000496120.231409239
+ 496120.210503740 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496120.210504116 DEV_SLEEP_TIME dev:8 wake:000496120.277497491
+ 496120.210505173 SLEEP sleep:000000000.020911748 longest_wake:000158140
+ 496120.231663695 WAKE num_fds:0
+ 496120.231687425 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496120.231718522 FILL_AUDIO dev:8 hw_level:2208
+ 496120.231723092 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.231723518 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.231725257 WRITE_STREAMS_MIXED write_limit:0
+ 496120.231728700 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496120.231737390 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496120.231737977 DEV_SLEEP_TIME dev:8 wake:000496120.277729958
+ 496120.231740066 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496120.231836699 WAKE num_fds:1
+ 496120.231867290 FILL_AUDIO dev:8 hw_level:2160
+ 496120.231869004 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.231870808 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.231874722 DEV_STREAM_MIX written:1024 read:1024
+ 496120.231875253 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.231876421 FILL_AUDIO_DONE hw_level:2160 total_written:1024 min_cb_level:1024
+ 496120.231877634 STREAM_SLEEP_TIME id:140000 wake:000496120.252742572
+ 496120.231883943 SET_DEV_WAKE dev:8 hw_level:3184 sleep:3184
+ 496120.231884314 DEV_SLEEP_TIME dev:8 wake:000496120.298210511
+ 496120.231885317 SLEEP sleep:000000000.020865394 longest_wake:000158140
+ 496120.252825725 WAKE num_fds:0
+ 496120.252847370 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496120.252873480 FILL_AUDIO dev:8 hw_level:2176
+ 496120.252877595 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.252878031 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.252879183 WRITE_STREAMS_MIXED write_limit:0
+ 496120.252882060 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496120.252889973 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496120.252890449 DEV_SLEEP_TIME dev:8 wake:000496120.298216616
+ 496120.252892264 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496120.252976333 WAKE num_fds:1
+ 496120.253004262 FILL_AUDIO dev:8 hw_level:2176
+ 496120.253005560 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.253007124 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.253011143 DEV_STREAM_MIX written:1024 read:1024
+ 496120.253011674 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.253012526 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496120.253013624 STREAM_SLEEP_TIME id:140000 wake:000496120.274075905
+ 496120.253019227 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496120.253019572 DEV_SLEEP_TIME dev:8 wake:000496120.319679829
+ 496120.253020595 SLEEP sleep:000000000.021062742 longest_wake:000158140
+ 496120.274106404 WAKE num_fds:0
+ 496120.274128715 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496120.274152144 FILL_AUDIO dev:8 hw_level:2192
+ 496120.274156384 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.274156855 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.274157943 WRITE_STREAMS_MIXED write_limit:0
+ 496120.274161110 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496120.274169534 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496120.274170056 DEV_SLEEP_TIME dev:8 wake:000496120.319829134
+ 496120.274171905 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496120.274253398 WAKE num_fds:1
+ 496120.274278085 FILL_AUDIO dev:8 hw_level:2192
+ 496120.274279458 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.274281037 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.274285166 DEV_STREAM_MIX written:1024 read:1024
+ 496120.274285718 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.274286800 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496120.274287888 STREAM_SLEEP_TIME id:140000 wake:000496120.295409238
+ 496120.274293445 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496120.274293796 DEV_SLEEP_TIME dev:8 wake:000496120.341287447
+ 496120.274295009 SLEEP sleep:000000000.021121791 longest_wake:000158140
+ 496120.295659600 WAKE num_fds:0
+ 496120.295682664 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496120.295713329 FILL_AUDIO dev:8 hw_level:2208
+ 496120.295717534 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.295718386 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.295720250 WRITE_STREAMS_MIXED write_limit:0
+ 496120.295723067 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496120.295731967 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496120.295732504 DEV_SLEEP_TIME dev:8 wake:000496120.341724505
+ 496120.295734333 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496120.295829954 WAKE num_fds:1
+ 496120.295858961 FILL_AUDIO dev:8 hw_level:2160
+ 496120.295860870 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.295862925 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.295867099 DEV_STREAM_MIX written:1024 read:1024
+ 496120.295867606 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.295868347 FILL_AUDIO_DONE hw_level:2160 total_written:1024 min_cb_level:1024
+ 496120.295869871 STREAM_SLEEP_TIME id:140000 wake:000496120.316742571
+ 496120.295876075 SET_DEV_WAKE dev:8 hw_level:3184 sleep:3184
+ 496120.295876441 DEV_SLEEP_TIME dev:8 wake:000496120.362202743
+ 496120.295877448 SLEEP sleep:000000000.020873161 longest_wake:000158140
+ 496120.317001495 WAKE num_fds:0
+ 496120.317024874 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496120.317055114 FILL_AUDIO dev:8 hw_level:2176
+ 496120.317059414 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.317060487 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.317062541 WRITE_STREAMS_MIXED write_limit:0
+ 496120.317065453 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496120.317073852 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496120.317074344 DEV_SLEEP_TIME dev:8 wake:000496120.362400199
+ 496120.317076313 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496120.317171929 WAKE num_fds:1
+ 496120.317202003 FILL_AUDIO dev:8 hw_level:2176
+ 496120.317203762 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.317205486 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.317209596 DEV_STREAM_MIX written:1024 read:1024
+ 496120.317210167 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.317211310 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496120.317212497 STREAM_SLEEP_TIME id:140000 wake:000496120.338075904
+ 496120.317218276 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496120.317218647 DEV_SLEEP_TIME dev:8 wake:000496120.383878722
+ 496120.317219604 SLEEP sleep:000000000.020863848 longest_wake:000158140
+ 496120.338325157 WAKE num_fds:0
+ 496120.338373825 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496120.338405919 FILL_AUDIO dev:8 hw_level:2192
+ 496120.338411276 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.338411737 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.338413556 WRITE_STREAMS_MIXED write_limit:0
+ 496120.338416749 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496120.338424973 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496120.338425564 DEV_SLEEP_TIME dev:8 wake:000496120.384084738
+ 496120.338427378 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496120.338521641 WAKE num_fds:1
+ 496120.338549681 FILL_AUDIO dev:8 hw_level:2192
+ 496120.338551405 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.338552868 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.338558997 DEV_STREAM_MIX written:1024 read:1024
+ 496120.338559533 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.338560415 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496120.338561518 STREAM_SLEEP_TIME id:140000 wake:000496120.359409237
+ 496120.338567281 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496120.338567632 DEV_SLEEP_TIME dev:8 wake:000496120.405561052
+ 496120.338568614 SLEEP sleep:000000000.020848185 longest_wake:000158140
+ 496120.359662607 WAKE num_fds:0
+ 496120.359686227 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496120.359718331 FILL_AUDIO dev:8 hw_level:2208
+ 496120.359722616 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.359723513 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.359724700 WRITE_STREAMS_MIXED write_limit:0
+ 496120.359727878 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496120.359736167 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496120.359736738 DEV_SLEEP_TIME dev:8 wake:000496120.405729211
+ 496120.359738628 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496120.359837621 WAKE num_fds:1
+ 496120.359869219 FILL_AUDIO dev:8 hw_level:2160
+ 496120.359871189 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.359873679 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.359877814 DEV_STREAM_MIX written:1024 read:1024
+ 496120.359878395 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.359879197 FILL_AUDIO_DONE hw_level:2160 total_written:1024 min_cb_level:1024
+ 496120.359880245 STREAM_SLEEP_TIME id:140000 wake:000496120.380742570
+ 496120.359885867 SET_DEV_WAKE dev:8 hw_level:3184 sleep:3184
+ 496120.359886213 DEV_SLEEP_TIME dev:8 wake:000496120.426213101
+ 496120.359887266 SLEEP sleep:000000000.020862802 longest_wake:000158140
+ 496120.380845520 WAKE num_fds:0
+ 496120.380869776 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496120.380902542 FILL_AUDIO dev:8 hw_level:2176
+ 496120.380906721 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.380907649 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.380909438 WRITE_STREAMS_MIXED write_limit:0
+ 496120.380912520 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496120.380921636 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496120.380922177 DEV_SLEEP_TIME dev:8 wake:000496120.426247958
+ 496120.380924212 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496120.381017252 WAKE num_fds:1
+ 496120.381045913 FILL_AUDIO dev:8 hw_level:2176
+ 496120.381048008 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.381049777 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.381053916 DEV_STREAM_MIX written:1024 read:1024
+ 496120.381054462 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.381055660 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496120.381057169 STREAM_SLEEP_TIME id:140000 wake:000496120.402075903
+ 496120.381063062 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496120.381063408 DEV_SLEEP_TIME dev:8 wake:000496120.447722903
+ 496120.381064390 SLEEP sleep:000000000.021019666 longest_wake:000158140
+ 496120.402140933 WAKE num_fds:0
+ 496120.402162097 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496120.402189415 FILL_AUDIO dev:8 hw_level:2192
+ 496120.402193519 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.402193985 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.402195153 WRITE_STREAMS_MIXED write_limit:0
+ 496120.402198355 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496120.402206043 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496120.402206589 DEV_SLEEP_TIME dev:8 wake:000496120.447866189
+ 496120.402208318 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496120.402282184 WAKE num_fds:1
+ 496120.402306199 FILL_AUDIO dev:8 hw_level:2192
+ 496120.402307868 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.402309497 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.402313747 DEV_STREAM_MIX written:1024 read:1024
+ 496120.402314258 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.402315240 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496120.402316273 STREAM_SLEEP_TIME id:140000 wake:000496120.423409236
+ 496120.402321866 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496120.402322251 DEV_SLEEP_TIME dev:8 wake:000496120.469315797
+ 496120.402323179 SLEEP sleep:000000000.021093439 longest_wake:000158140
+ 496120.423449244 WAKE num_fds:0
+ 496120.423476422 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496120.423503059 FILL_AUDIO dev:8 hw_level:2208
+ 496120.423507604 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.423508090 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.423509418 WRITE_STREAMS_MIXED write_limit:0
+ 496120.423512510 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496120.423520454 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496120.423520975 DEV_SLEEP_TIME dev:8 wake:000496120.469513698
+ 496120.423522774 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496120.423600017 WAKE num_fds:1
+ 496120.423628152 FILL_AUDIO dev:8 hw_level:2208
+ 496120.423630277 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.423632317 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.423639759 DEV_STREAM_MIX written:1024 read:1024
+ 496120.423640521 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.423642105 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496120.423643353 STREAM_SLEEP_TIME id:140000 wake:000496120.444742569
+ 496120.423650028 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496120.423650594 DEV_SLEEP_TIME dev:8 wake:000496120.490976119
+ 496120.423651992 SLEEP sleep:000000000.021099783 longest_wake:000158140
+ 496120.444785897 WAKE num_fds:0
+ 496120.444805768 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496120.444826351 FILL_AUDIO dev:8 hw_level:2176
+ 496120.444830240 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.444830731 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.444831889 WRITE_STREAMS_MIXED write_limit:0
+ 496120.444835256 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496120.444842919 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496120.444843495 DEV_SLEEP_TIME dev:8 wake:000496120.490169752
+ 496120.444845274 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496120.444910946 WAKE num_fds:1
+ 496120.444933318 FILL_AUDIO dev:8 hw_level:2176
+ 496120.444934841 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.444936269 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.444940083 DEV_STREAM_MIX written:1024 read:1024
+ 496120.444940655 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.444941451 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496120.444942519 STREAM_SLEEP_TIME id:140000 wake:000496120.466075902
+ 496120.444947921 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496120.444948317 DEV_SLEEP_TIME dev:8 wake:000496120.511608689
+ 496120.444949335 SLEEP sleep:000000000.021133879 longest_wake:000158140
+ 496120.466115369 WAKE num_fds:0
+ 496120.466135761 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496120.466155045 FILL_AUDIO dev:8 hw_level:2192
+ 496120.466159004 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.466159506 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.466160548 WRITE_STREAMS_MIXED write_limit:0
+ 496120.466163319 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496120.466170927 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496120.466171518 DEV_SLEEP_TIME dev:8 wake:000496120.511831198
+ 496120.466173272 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496120.466238388 WAKE num_fds:1
+ 496120.466260313 FILL_AUDIO dev:8 hw_level:2192
+ 496120.466261681 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.466263085 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.466268868 DEV_STREAM_MIX written:1024 read:1024
+ 496120.466269444 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.466270301 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496120.466271249 STREAM_SLEEP_TIME id:140000 wake:000496120.487409235
+ 496120.466276636 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496120.466276992 DEV_SLEEP_TIME dev:8 wake:000496120.533270767
+ 496120.466278014 SLEEP sleep:000000000.021138468 longest_wake:000158140
+ 496120.487490685 WAKE num_fds:0
+ 496120.487512195 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496120.487536877 FILL_AUDIO dev:8 hw_level:2208
+ 496120.487540956 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.487541387 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.487542424 WRITE_STREAMS_MIXED write_limit:0
+ 496120.487545241 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496120.487553319 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496120.487553801 DEV_SLEEP_TIME dev:8 wake:000496120.533546479
+ 496120.487555795 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496120.487637278 WAKE num_fds:1
+ 496120.487664200 FILL_AUDIO dev:8 hw_level:2208
+ 496120.487665549 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.487667388 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.487671317 DEV_STREAM_MIX written:1024 read:1024
+ 496120.487671903 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.487672976 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496120.487673953 STREAM_SLEEP_TIME id:140000 wake:000496120.508742568
+ 496120.487679601 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496120.487679967 DEV_SLEEP_TIME dev:8 wake:000496120.555006830
+ 496120.487680884 SLEEP sleep:000000000.021069071 longest_wake:000158140
+ 496120.508772677 WAKE num_fds:0
+ 496120.508795374 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496120.508815400 FILL_AUDIO dev:8 hw_level:2224
+ 496120.508819660 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.508820161 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.508821374 WRITE_STREAMS_MIXED write_limit:0
+ 496120.508824196 FILL_AUDIO_DONE hw_level:2224 total_written:0 min_cb_level:1024
+ 496120.508832600 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496120.508833091 DEV_SLEEP_TIME dev:8 wake:000496120.554158651
+ 496120.508834880 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496120.508902311 WAKE num_fds:1
+ 496120.508923881 FILL_AUDIO dev:8 hw_level:2176
+ 496120.508925309 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.508926873 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.508931017 DEV_STREAM_MIX written:1024 read:1024
+ 496120.508931624 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.508932751 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496120.508933844 STREAM_SLEEP_TIME id:140000 wake:000496120.530075901
+ 496120.508939532 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496120.508939878 DEV_SLEEP_TIME dev:8 wake:000496120.575600019
+ 496120.508940905 SLEEP sleep:000000000.021142548 longest_wake:000158140
+ 496120.530106994 WAKE num_fds:0
+ 496120.530126138 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496120.530149222 FILL_AUDIO dev:8 hw_level:2192
+ 496120.530153271 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.530153772 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.530154915 WRITE_STREAMS_MIXED write_limit:0
+ 496120.530157902 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496120.530166151 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496120.530166632 DEV_SLEEP_TIME dev:8 wake:000496120.575825851
+ 496120.530168476 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496120.530263270 WAKE num_fds:1
+ 496120.530287591 FILL_AUDIO dev:8 hw_level:2192
+ 496120.530289390 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.530291224 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.530297243 DEV_STREAM_MIX written:1024 read:1024
+ 496120.530298100 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.530299073 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496120.530300030 STREAM_SLEEP_TIME id:140000 wake:000496120.551409234
+ 496120.530305537 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496120.530305923 DEV_SLEEP_TIME dev:8 wake:000496120.597299604
+ 496120.530306931 SLEEP sleep:000000000.021109630 longest_wake:000158140
+ 496120.551515216 WAKE num_fds:0
+ 496120.551539643 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496120.551571982 FILL_AUDIO dev:8 hw_level:2208
+ 496120.551576849 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.551577670 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.551578848 WRITE_STREAMS_MIXED write_limit:0
+ 496120.551581605 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496120.551589884 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496120.551590535 DEV_SLEEP_TIME dev:8 wake:000496120.597582882
+ 496120.551592309 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496120.551687780 WAKE num_fds:1
+ 496120.551717463 FILL_AUDIO dev:8 hw_level:2208
+ 496120.551719322 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.551720916 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.551725036 DEV_STREAM_MIX written:1024 read:1024
+ 496120.551725582 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.551726554 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496120.551727752 STREAM_SLEEP_TIME id:140000 wake:000496120.572742567
+ 496120.551733861 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496120.551734207 DEV_SLEEP_TIME dev:8 wake:000496120.619060629
+ 496120.551735425 SLEEP sleep:000000000.021015271 longest_wake:000158140
+ 496120.572824676 WAKE num_fds:0
+ 496120.572845770 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496120.572870968 FILL_AUDIO dev:8 hw_level:2176
+ 496120.572875679 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.572876100 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.572877102 WRITE_STREAMS_MIXED write_limit:0
+ 496120.572880210 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496120.572888138 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496120.572888634 DEV_SLEEP_TIME dev:8 wake:000496120.618214821
+ 496120.572890538 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496120.572971871 WAKE num_fds:1
+ 496120.572998568 FILL_AUDIO dev:8 hw_level:2176
+ 496120.573000197 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.573001775 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.573005724 DEV_STREAM_MIX written:1024 read:1024
+ 496120.573006361 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.573007188 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496120.573008170 STREAM_SLEEP_TIME id:140000 wake:000496120.594075900
+ 496120.573013723 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496120.573014129 DEV_SLEEP_TIME dev:8 wake:000496120.639674380
+ 496120.573015131 SLEEP sleep:000000000.021068186 longest_wake:000158140
+ 496120.594106428 WAKE num_fds:0
+ 496120.594130167 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496120.594152995 FILL_AUDIO dev:8 hw_level:2192
+ 496120.594157060 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.594157621 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.594158964 WRITE_STREAMS_MIXED write_limit:0
+ 496120.594162472 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496120.594171398 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496120.594171974 DEV_SLEEP_TIME dev:8 wake:000496120.639830446
+ 496120.594173888 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496120.594300360 WAKE num_fds:1
+ 496120.594345318 FILL_AUDIO dev:8 hw_level:2192
+ 496120.594349362 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.594351888 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.594357862 DEV_STREAM_MIX written:1024 read:1024
+ 496120.594359015 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.594360694 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496120.594362272 STREAM_SLEEP_TIME id:140000 wake:000496120.615409233
+ 496120.594372245 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496120.594372631 DEV_SLEEP_TIME dev:8 wake:000496120.661361646
+ 496120.594373869 SLEEP sleep:000000000.021047587 longest_wake:000158140
+ 496120.615497480 WAKE num_fds:0
+ 496120.615518910 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496120.615544870 FILL_AUDIO dev:8 hw_level:2208
+ 496120.615549009 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.615549485 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.615550703 WRITE_STREAMS_MIXED write_limit:0
+ 496120.615553650 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496120.615561789 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496120.615562275 DEV_SLEEP_TIME dev:8 wake:000496120.661554928
+ 496120.615564194 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496120.615648213 WAKE num_fds:1
+ 496120.615675972 FILL_AUDIO dev:8 hw_level:2208
+ 496120.615677651 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.615679205 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.615685173 DEV_STREAM_MIX written:1024 read:1024
+ 496120.615685690 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.615686592 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496120.615687614 STREAM_SLEEP_TIME id:140000 wake:000496120.636742566
+ 496120.615693242 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496120.615693638 DEV_SLEEP_TIME dev:8 wake:000496120.683020481
+ 496120.615694630 SLEEP sleep:000000000.021055418 longest_wake:000158140
+ 496120.636988830 WAKE num_fds:0
+ 496120.637012740 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496120.637044844 FILL_AUDIO dev:8 hw_level:2176
+ 496120.637049500 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.637050006 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.637051670 WRITE_STREAMS_MIXED write_limit:0
+ 496120.637055198 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496120.637063793 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496120.637064369 DEV_SLEEP_TIME dev:8 wake:000496120.682389804
+ 496120.637066249 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496120.637165513 WAKE num_fds:1
+ 496120.637197221 FILL_AUDIO dev:8 hw_level:2176
+ 496120.637199186 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.637200739 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.637204809 DEV_STREAM_MIX written:1024 read:1024
+ 496120.637205505 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.637206292 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496120.637207460 STREAM_SLEEP_TIME id:140000 wake:000496120.658075899
+ 496120.637213203 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496120.637213549 DEV_SLEEP_TIME dev:8 wake:000496120.703873655
+ 496120.637214581 SLEEP sleep:000000000.020868910 longest_wake:000158140
+ 496120.658107059 WAKE num_fds:0
+ 496120.658131099 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496120.658156964 FILL_AUDIO dev:8 hw_level:2192
+ 496120.658161169 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.658161655 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.658162793 WRITE_STREAMS_MIXED write_limit:0
+ 496120.658165679 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496120.658173658 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496120.658174189 DEV_SLEEP_TIME dev:8 wake:000496120.703833628
+ 496120.658176143 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496120.658282023 WAKE num_fds:1
+ 496120.658308744 FILL_AUDIO dev:8 hw_level:2192
+ 496120.658310418 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.658312052 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.658315991 DEV_STREAM_MIX written:1024 read:1024
+ 496120.658316542 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.658317565 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496120.658318632 STREAM_SLEEP_TIME id:140000 wake:000496120.679409232
+ 496120.658324140 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496120.658324521 DEV_SLEEP_TIME dev:8 wake:000496120.725318191
+ 496120.658325598 SLEEP sleep:000000000.021091041 longest_wake:000158140
+ 496120.679472577 WAKE num_fds:0
+ 496120.679499710 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496120.679534876 FILL_AUDIO dev:8 hw_level:2208
+ 496120.679539322 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.679539768 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.679540820 WRITE_STREAMS_MIXED write_limit:0
+ 496120.679543962 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496120.679552066 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496120.679552552 DEV_SLEEP_TIME dev:8 wake:000496120.725545526
+ 496120.679554717 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496120.679658206 WAKE num_fds:1
+ 496120.679687063 FILL_AUDIO dev:8 hw_level:2208
+ 496120.679689233 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.679691282 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.679696504 DEV_STREAM_MIX written:1024 read:1024
+ 496120.679697211 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.679698694 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496120.679700002 STREAM_SLEEP_TIME id:140000 wake:000496120.700742565
+ 496120.679706392 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496120.679706863 DEV_SLEEP_TIME dev:8 wake:000496120.747032699
+ 496120.679708442 SLEEP sleep:000000000.021043199 longest_wake:000158140
+ 496120.701001735 WAKE num_fds:0
+ 496120.701028687 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496120.701068313 FILL_AUDIO dev:8 hw_level:2176
+ 496120.701073981 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.701074483 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.701075831 WRITE_STREAMS_MIXED write_limit:0
+ 496120.701078873 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496120.701087443 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496120.701088089 DEV_SLEEP_TIME dev:8 wake:000496120.746413599
+ 496120.701089998 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496120.701178107 WAKE num_fds:1
+ 496120.701210877 FILL_AUDIO dev:8 hw_level:2176
+ 496120.701214907 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.701217107 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.701224354 DEV_STREAM_MIX written:1024 read:1024
+ 496120.701225426 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.701226919 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496120.701228268 STREAM_SLEEP_TIME id:140000 wake:000496120.722075898
+ 496120.701235750 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496120.701236226 DEV_SLEEP_TIME dev:8 wake:000496120.767894327
+ 496120.701237489 SLEEP sleep:000000000.020848237 longest_wake:000158140
+ 496120.722129751 WAKE num_fds:0
+ 496120.722152443 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496120.722180463 FILL_AUDIO dev:8 hw_level:2192
+ 496120.722184517 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.722185986 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.722187118 WRITE_STREAMS_MIXED write_limit:0
+ 496120.722190125 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496120.722198114 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496120.722198590 DEV_SLEEP_TIME dev:8 wake:000496120.767858014
+ 496120.722200319 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496120.722290758 WAKE num_fds:1
+ 496120.722319033 FILL_AUDIO dev:8 hw_level:2192
+ 496120.722320817 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.722322326 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.722343188 DEV_STREAM_MIX written:1024 read:1024
+ 496120.722344035 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.722345638 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496120.722347122 STREAM_SLEEP_TIME id:140000 wake:000496120.743409231
+ 496120.722354043 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496120.722354504 DEV_SLEEP_TIME dev:8 wake:000496120.789346455
+ 496120.722355882 SLEEP sleep:000000000.021062776 longest_wake:000158140
+ 496120.743467496 WAKE num_fds:0
+ 496120.743516138 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496120.743566865 FILL_AUDIO dev:8 hw_level:2208
+ 496120.743580211 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.743581248 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.743582621 WRITE_STREAMS_MIXED write_limit:0
+ 496120.743591356 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496120.743609794 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496120.743611438 DEV_SLEEP_TIME dev:8 wake:000496120.789595295
+ 496120.743614821 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496120.743749752 WAKE num_fds:1
+ 496120.743794781 FILL_AUDIO dev:8 hw_level:2208
+ 496120.743800940 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.743806643 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.743815474 DEV_STREAM_MIX written:1024 read:1024
+ 496120.743816401 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.743820355 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496120.743821543 STREAM_SLEEP_TIME id:140000 wake:000496120.764742564
+ 496120.743832989 SET_DEV_WAKE dev:8 hw_level:3184 sleep:3184
+ 496120.743833335 DEV_SLEEP_TIME dev:8 wake:000496120.810154400
+ 496120.743834934 SLEEP sleep:000000000.020921497 longest_wake:000158140
+ 496120.764995124 WAKE num_fds:0
+ 496120.765018142 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496120.765049405 FILL_AUDIO dev:8 hw_level:2176
+ 496120.765054130 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.765054607 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.765055724 WRITE_STREAMS_MIXED write_limit:0
+ 496120.765059077 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496120.765067501 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496120.765068063 DEV_SLEEP_TIME dev:8 wake:000496120.810393783
+ 496120.765069787 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496120.765170805 WAKE num_fds:1
+ 496120.765200934 FILL_AUDIO dev:8 hw_level:2176
+ 496120.765203149 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.765205049 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.765209123 DEV_STREAM_MIX written:1024 read:1024
+ 496120.765209710 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.765210597 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496120.765211584 STREAM_SLEEP_TIME id:140000 wake:000496120.786075897
+ 496120.765217232 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496120.765217583 DEV_SLEEP_TIME dev:8 wake:000496120.831877784
+ 496120.765218565 SLEEP sleep:000000000.020864779 longest_wake:000158140
+ 496120.786359235 WAKE num_fds:0
+ 496120.786386748 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496120.786424054 FILL_AUDIO dev:8 hw_level:2192
+ 496120.786429051 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.786429712 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.786430840 WRITE_STREAMS_MIXED write_limit:0
+ 496120.786434593 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496120.786442146 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496120.786442732 DEV_SLEEP_TIME dev:8 wake:000496120.832102517
+ 496120.786444822 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496120.786626827 WAKE num_fds:1
+ 496120.786663677 FILL_AUDIO dev:8 hw_level:2192
+ 496120.786666764 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.786669656 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.786676146 DEV_STREAM_MIX written:1024 read:1024
+ 496120.786676853 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.786678446 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496120.786679774 STREAM_SLEEP_TIME id:140000 wake:000496120.807409230
+ 496120.786686410 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496120.786686836 DEV_SLEEP_TIME dev:8 wake:000496120.853679218
+ 496120.786688139 SLEEP sleep:000000000.020730012 longest_wake:000158140
+ 496120.807445429 WAKE num_fds:0
+ 496120.807468548 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496120.807488058 FILL_AUDIO dev:8 hw_level:2208
+ 496120.807491601 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.807491967 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.807492949 WRITE_STREAMS_MIXED write_limit:0
+ 496120.807495610 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496120.807502712 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496120.807503133 DEV_SLEEP_TIME dev:8 wake:000496120.853496708
+ 496120.807504536 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496120.807594052 WAKE num_fds:1
+ 496120.807615998 FILL_AUDIO dev:8 hw_level:2208
+ 496120.807617141 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.807618714 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.807623114 DEV_STREAM_MIX written:1024 read:1024
+ 496120.807623711 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.807624498 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496120.807625550 STREAM_SLEEP_TIME id:140000 wake:000496120.828742563
+ 496120.807631148 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496120.807631539 DEV_SLEEP_TIME dev:8 wake:000496120.874958417
+ 496120.807632491 SLEEP sleep:000000000.021117479 longest_wake:000158140
+ 496120.828792742 WAKE num_fds:0
+ 496120.828820235 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496120.828856364 FILL_AUDIO dev:8 hw_level:2176
+ 496120.828863094 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.828863861 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.828865430 WRITE_STREAMS_MIXED write_limit:0
+ 496120.828869083 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496120.828879402 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496120.828880214 DEV_SLEEP_TIME dev:8 wake:000496120.874204110
+ 496120.828883105 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496120.829016984 WAKE num_fds:1
+ 496120.829054035 FILL_AUDIO dev:8 hw_level:2176
+ 496120.829056892 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.829058781 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.829064614 DEV_STREAM_MIX written:1024 read:1024
+ 496120.829065461 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.829067341 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496120.829069421 STREAM_SLEEP_TIME id:140000 wake:000496120.850075896
+ 496120.829076051 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496120.829076938 DEV_SLEEP_TIME dev:8 wake:000496120.895735420
+ 496120.829078632 SLEEP sleep:000000000.021007142 longest_wake:000158140
+ 496120.850120456 WAKE num_fds:0
+ 496120.850146552 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496120.850169236 FILL_AUDIO dev:8 hw_level:2192
+ 496120.850174064 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.850174484 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.850175535 WRITE_STREAMS_MIXED write_limit:0
+ 496120.850178863 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496120.850186829 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496120.850187290 DEV_SLEEP_TIME dev:8 wake:000496120.895846740
+ 496120.850189190 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496120.850269175 WAKE num_fds:1
+ 496120.850295709 FILL_AUDIO dev:8 hw_level:2192
+ 496120.850297505 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.850299301 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.850306373 DEV_STREAM_MIX written:1024 read:1024
+ 496120.850307146 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.850308429 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496120.850309942 STREAM_SLEEP_TIME id:140000 wake:000496120.871409229
+ 496120.850316384 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496120.850316865 DEV_SLEEP_TIME dev:8 wake:000496120.917309275
+ 496120.850318385 SLEEP sleep:000000000.021099954 longest_wake:000158140
+ 496120.871515551 WAKE num_fds:0
+ 496120.871539366 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496120.871569746 FILL_AUDIO dev:8 hw_level:2208
+ 496120.871573680 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.871574046 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.871574918 WRITE_STREAMS_MIXED write_limit:0
+ 496120.871578080 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496120.871584976 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496120.871585402 DEV_SLEEP_TIME dev:8 wake:000496120.917579043
+ 496120.871586780 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496120.871750112 WAKE num_fds:1
+ 496120.871778648 FILL_AUDIO dev:8 hw_level:2208
+ 496120.871780162 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.871781846 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.871788481 DEV_STREAM_MIX written:1024 read:1024
+ 496120.871789102 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.871790060 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496120.871791287 STREAM_SLEEP_TIME id:140000 wake:000496120.892742562
+ 496120.871797371 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496120.871797833 DEV_SLEEP_TIME dev:8 wake:000496120.939124029
+ 496120.871799045 SLEEP sleep:000000000.020951866 longest_wake:000158140
+ 496120.892801211 WAKE num_fds:0
+ 496120.892847132 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496120.892888528 FILL_AUDIO dev:8 hw_level:2176
+ 496120.892896396 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.892897123 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.892898987 WRITE_STREAMS_MIXED write_limit:0
+ 496120.892904364 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496120.892913505 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496120.892914112 DEV_SLEEP_TIME dev:8 wake:000496120.938239276
+ 496120.892917434 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496120.893084380 WAKE num_fds:1
+ 496120.893127394 FILL_AUDIO dev:8 hw_level:2176
+ 496120.893129760 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.893131584 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.893139863 DEV_STREAM_MIX written:1024 read:1024
+ 496120.893140660 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.893154903 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496120.893156266 STREAM_SLEEP_TIME id:140000 wake:000496120.914075895
+ 496120.893162600 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496120.893163157 DEV_SLEEP_TIME dev:8 wake:000496120.959822335
+ 496120.893164620 SLEEP sleep:000000000.020920226 longest_wake:000158140
+ 496120.914118665 WAKE num_fds:0
+ 496120.914145071 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496120.914177055 FILL_AUDIO dev:8 hw_level:2192
+ 496120.914181194 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.914181555 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.914182592 WRITE_STREAMS_MIXED write_limit:0
+ 496120.914185198 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496120.914192555 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496120.914192976 DEV_SLEEP_TIME dev:8 wake:000496120.959852962
+ 496120.914194495 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496120.914342014 WAKE num_fds:1
+ 496120.914374870 FILL_AUDIO dev:8 hw_level:2192
+ 496120.914377005 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.914378849 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.914384623 DEV_STREAM_MIX written:1024 read:1024
+ 496120.914385279 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.914386522 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496120.914387710 STREAM_SLEEP_TIME id:140000 wake:000496120.935409228
+ 496120.914393338 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496120.914393764 DEV_SLEEP_TIME dev:8 wake:000496120.981387174
+ 496120.914395007 SLEEP sleep:000000000.021022054 longest_wake:000158140
+ 496120.935443284 WAKE num_fds:0
+ 496120.935466292 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496120.935494723 FILL_AUDIO dev:8 hw_level:2208
+ 496120.935498757 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.935499143 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.935500250 WRITE_STREAMS_MIXED write_limit:0
+ 496120.935502811 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496120.935509858 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496120.935510274 DEV_SLEEP_TIME dev:8 wake:000496120.981503844
+ 496120.935512088 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496120.935671401 WAKE num_fds:1
+ 496120.935705795 FILL_AUDIO dev:8 hw_level:2208
+ 496120.935708366 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.935709944 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.935715983 DEV_STREAM_MIX written:1024 read:1024
+ 496120.935716700 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.935718204 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496120.935719502 STREAM_SLEEP_TIME id:140000 wake:000496120.956742561
+ 496120.935725365 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496120.935725731 DEV_SLEEP_TIME dev:8 wake:000496121.003052283
+ 496120.935726974 SLEEP sleep:000000000.021023611 longest_wake:000158140
+ 496120.956811014 WAKE num_fds:0
+ 496120.956833380 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496120.956857200 FILL_AUDIO dev:8 hw_level:2176
+ 496120.956860568 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.956860934 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.956861791 WRITE_STREAMS_MIXED write_limit:0
+ 496120.956864337 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496120.956871167 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496120.956871538 DEV_SLEEP_TIME dev:8 wake:000496121.002198642
+ 496120.956872942 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496120.957023259 WAKE num_fds:1
+ 496120.957050822 FILL_AUDIO dev:8 hw_level:2176
+ 496120.957052085 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.957053584 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.957059472 DEV_STREAM_MIX written:1024 read:1024
+ 496120.957060068 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.957060795 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496120.957061727 STREAM_SLEEP_TIME id:140000 wake:000496120.978075894
+ 496120.957067446 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496120.957067826 DEV_SLEEP_TIME dev:8 wake:000496121.023727977
+ 496120.957068839 SLEEP sleep:000000000.021014583 longest_wake:000158140
+ 496120.978110225 WAKE num_fds:0
+ 496120.978133193 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496120.978157349 FILL_AUDIO dev:8 hw_level:2192
+ 496120.978161774 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.978162401 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.978163603 WRITE_STREAMS_MIXED write_limit:0
+ 496120.978166410 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496120.978174498 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496120.978175055 DEV_SLEEP_TIME dev:8 wake:000496121.023834404
+ 496120.978176864 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496120.978294115 WAKE num_fds:1
+ 496120.978341573 FILL_AUDIO dev:8 hw_level:2192
+ 496120.978344284 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.978345853 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.978350043 DEV_STREAM_MIX written:1024 read:1024
+ 496120.978350724 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.978352358 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496120.978353656 STREAM_SLEEP_TIME id:140000 wake:000496120.999409227
+ 496120.978359555 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496120.978359931 DEV_SLEEP_TIME dev:8 wake:000496121.045353110
+ 496120.978361198 SLEEP sleep:000000000.021056117 longest_wake:000158140
+ 496120.999438377 WAKE num_fds:0
+ 496120.999457772 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496120.999481026 FILL_AUDIO dev:8 hw_level:2208
+ 496120.999484323 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496120.999484689 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496120.999485541 WRITE_STREAMS_MIXED write_limit:0
+ 496120.999487892 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496120.999494517 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496120.999494883 DEV_SLEEP_TIME dev:8 wake:000496121.045488844
+ 496120.999496361 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496120.999657453 WAKE num_fds:1
+ 496120.999688670 FILL_AUDIO dev:8 hw_level:2208
+ 496120.999691136 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496120.999692664 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496120.999698443 DEV_STREAM_MIX written:1024 read:1024
+ 496120.999699154 WRITE_STREAMS_MIXED write_limit:1024
+ 496120.999700618 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496120.999701906 STREAM_SLEEP_TIME id:140000 wake:000496121.020742560
+ 496120.999707408 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496120.999707779 DEV_SLEEP_TIME dev:8 wake:000496121.067034697
+ 496120.999709032 SLEEP sleep:000000000.021041196 longest_wake:000158140
+ 496121.020803596 WAKE num_fds:0
+ 496121.020825632 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496121.020845974 FILL_AUDIO dev:8 hw_level:2176
+ 496121.020849552 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.020849963 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.020850860 WRITE_STREAMS_MIXED write_limit:0
+ 496121.020853421 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496121.020860498 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496121.020860919 DEV_SLEEP_TIME dev:8 wake:000496121.066187902
+ 496121.020862302 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496121.021012834 WAKE num_fds:1
+ 496121.021038699 FILL_AUDIO dev:8 hw_level:2176
+ 496121.021040107 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.021041651 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.021047835 DEV_STREAM_MIX written:1024 read:1024
+ 496121.021048461 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.021049830 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:0
+ 496121.021050226 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.021050727 WRITE_STREAMS_MIXED write_limit:0
+ 496121.021051388 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496121.021052396 STREAM_SLEEP_TIME id:140000 wake:000496121.042075893
+ 496121.021057968 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496121.021058349 DEV_SLEEP_TIME dev:8 wake:000496121.087718621
+ 496121.021059432 SLEEP sleep:000000000.021023938 longest_wake:000158140
+ 496121.042115492 WAKE num_fds:0
+ 496121.042141532 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496121.042168459 FILL_AUDIO dev:8 hw_level:2192
+ 496121.042173215 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.042173726 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.042174779 WRITE_STREAMS_MIXED write_limit:0
+ 496121.042177791 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496121.042186050 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496121.042186571 DEV_SLEEP_TIME dev:8 wake:000496121.087845660
+ 496121.042188430 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496121.042299602 WAKE num_fds:1
+ 496121.042374133 FILL_AUDIO dev:8 hw_level:2192
+ 496121.042379581 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.042383896 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.042396134 DEV_STREAM_MIX written:1024 read:1024
+ 496121.042396845 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.042399356 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496121.042401336 STREAM_SLEEP_TIME id:140000 wake:000496121.063409226
+ 496121.042408733 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496121.042409194 DEV_SLEEP_TIME dev:8 wake:000496121.109400118
+ 496121.042411098 SLEEP sleep:000000000.021009108 longest_wake:000158140
+ 496121.063442196 WAKE num_fds:0
+ 496121.063463530 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496121.063487531 FILL_AUDIO dev:8 hw_level:2208
+ 496121.063490733 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.063491094 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.063491956 WRITE_STREAMS_MIXED write_limit:0
+ 496121.063494311 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496121.063501097 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496121.063501518 DEV_SLEEP_TIME dev:8 wake:000496121.109495334
+ 496121.063502906 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496121.063638629 WAKE num_fds:1
+ 496121.063664449 FILL_AUDIO dev:8 hw_level:2208
+ 496121.063665983 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.063667807 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.063672392 DEV_STREAM_MIX written:1024 read:1024
+ 496121.063672979 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.063673881 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496121.063674803 STREAM_SLEEP_TIME id:140000 wake:000496121.084742559
+ 496121.063680601 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496121.063680947 DEV_SLEEP_TIME dev:8 wake:000496121.131007700
+ 496121.063681985 SLEEP sleep:000000000.021068192 longest_wake:000158140
+ 496121.084972982 WAKE num_fds:0
+ 496121.084998972 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496121.085036704 FILL_AUDIO dev:8 hw_level:2176
+ 496121.085042467 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.085043004 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.085044231 WRITE_STREAMS_MIXED write_limit:0
+ 496121.085047218 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496121.085055317 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496121.085055828 DEV_SLEEP_TIME dev:8 wake:000496121.130381869
+ 496121.085057853 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496121.085230672 WAKE num_fds:1
+ 496121.085267442 FILL_AUDIO dev:8 hw_level:2176
+ 496121.085269406 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.085270950 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.085275450 DEV_STREAM_MIX written:1024 read:1024
+ 496121.085276072 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.085277325 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496121.085278387 STREAM_SLEEP_TIME id:140000 wake:000496121.106075892
+ 496121.085284005 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496121.085284386 DEV_SLEEP_TIME dev:8 wake:000496121.151944572
+ 496121.085285343 SLEEP sleep:000000000.020797986 longest_wake:000158140
+ 496121.106153725 WAKE num_fds:0
+ 496121.106176578 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496121.106205204 FILL_AUDIO dev:8 hw_level:2192
+ 496121.106209083 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.106209449 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.106210311 WRITE_STREAMS_MIXED write_limit:0
+ 496121.106212756 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496121.106219677 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496121.106220093 DEV_SLEEP_TIME dev:8 wake:000496121.151880430
+ 496121.106221497 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496121.106405836 WAKE num_fds:1
+ 496121.106434848 FILL_AUDIO dev:8 hw_level:2192
+ 496121.106436587 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.106438196 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.106442836 DEV_STREAM_MIX written:1024 read:1024
+ 496121.106443453 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.106444270 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496121.106445267 STREAM_SLEEP_TIME id:140000 wake:000496121.127409225
+ 496121.106450840 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496121.106451211 DEV_SLEEP_TIME dev:8 wake:000496121.173444846
+ 496121.106452173 SLEEP sleep:000000000.020964379 longest_wake:000158140
+ 496121.127456103 WAKE num_fds:0
+ 496121.127480189 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496121.127509316 FILL_AUDIO dev:8 hw_level:2208
+ 496121.127514788 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.127515350 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.127516447 WRITE_STREAMS_MIXED write_limit:0
+ 496121.127519494 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496121.127527327 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496121.127527949 DEV_SLEEP_TIME dev:8 wake:000496121.173520722
+ 496121.127529863 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496121.127623489 WAKE num_fds:1
+ 496121.127651173 FILL_AUDIO dev:8 hw_level:2208
+ 496121.127653223 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.127655077 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.127661482 DEV_STREAM_MIX written:1024 read:1024
+ 496121.127662299 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.127663462 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496121.127664910 STREAM_SLEEP_TIME id:140000 wake:000496121.148742558
+ 496121.127671079 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496121.127671600 DEV_SLEEP_TIME dev:8 wake:000496121.194997566
+ 496121.127673084 SLEEP sleep:000000000.021078325 longest_wake:000158140
+ 496121.148794283 WAKE num_fds:0
+ 496121.148808400 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496121.148823722 FILL_AUDIO dev:8 hw_level:2176
+ 496121.148826017 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.148826191 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.148826634 WRITE_STREAMS_MIXED write_limit:0
+ 496121.148828005 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496121.148833984 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496121.148834180 DEV_SLEEP_TIME dev:8 wake:000496121.194161902
+ 496121.148834991 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496121.148928624 WAKE num_fds:1
+ 496121.148951279 FILL_AUDIO dev:8 hw_level:2176
+ 496121.148952642 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.148953524 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.148957366 DEV_STREAM_MIX written:1024 read:1024
+ 496121.148957752 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.148958498 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496121.148959151 STREAM_SLEEP_TIME id:140000 wake:000496121.170075891
+ 496121.148964100 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496121.148964295 DEV_SLEEP_TIME dev:8 wake:000496121.215625558
+ 496121.148965082 SLEEP sleep:000000000.021116999 longest_wake:000158140
+ 496121.170295036 WAKE num_fds:0
+ 496121.170310195 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496121.170338189 FILL_AUDIO dev:8 hw_level:2192
+ 496121.170340356 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.170340526 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.170341064 WRITE_STREAMS_MIXED write_limit:0
+ 496121.170342450 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496121.170348111 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496121.170348306 DEV_SLEEP_TIME dev:8 wake:000496121.216009708
+ 496121.170349021 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496121.170436685 WAKE num_fds:1
+ 496121.170457636 FILL_AUDIO dev:8 hw_level:2192
+ 496121.170458990 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.170459889 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.170463374 DEV_STREAM_MIX written:1024 read:1024
+ 496121.170463768 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.170464518 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496121.170465104 STREAM_SLEEP_TIME id:140000 wake:000496121.191409224
+ 496121.170469940 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496121.170470140 DEV_SLEEP_TIME dev:8 wake:000496121.237464872
+ 496121.170470759 SLEEP sleep:000000000.020944352 longest_wake:000158140
+ 496121.191442767 WAKE num_fds:0
+ 496121.191468065 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496121.191492321 FILL_AUDIO dev:8 hw_level:2208
+ 496121.191497173 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.191497739 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.191498982 WRITE_STREAMS_MIXED write_limit:0
+ 496121.191502044 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496121.191510178 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496121.191510804 DEV_SLEEP_TIME dev:8 wake:000496121.237503387
+ 496121.191512688 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496121.191656004 WAKE num_fds:1
+ 496121.191683488 FILL_AUDIO dev:8 hw_level:2208
+ 496121.191685171 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.191687051 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.191693110 DEV_STREAM_MIX written:1024 read:1024
+ 496121.191693912 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.191694869 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496121.191696172 STREAM_SLEEP_TIME id:140000 wake:000496121.212742557
+ 496121.191702466 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496121.191703003 DEV_SLEEP_TIME dev:8 wake:000496121.259028934
+ 496121.191704526 SLEEP sleep:000000000.021046956 longest_wake:000158140
+ 496121.212804603 WAKE num_fds:0
+ 496121.212826428 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496121.212845046 FILL_AUDIO dev:8 hw_level:2176
+ 496121.212848564 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.212848930 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.212849963 WRITE_STREAMS_MIXED write_limit:0
+ 496121.212852378 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496121.212859840 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496121.212860281 DEV_SLEEP_TIME dev:8 wake:000496121.258187200
+ 496121.212861805 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496121.213036739 WAKE num_fds:1
+ 496121.213070923 FILL_AUDIO dev:8 hw_level:2176
+ 496121.213073554 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.213075102 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.213081507 DEV_STREAM_MIX written:1024 read:1024
+ 496121.213082214 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.213083777 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496121.213085085 STREAM_SLEEP_TIME id:140000 wake:000496121.234075890
+ 496121.213090939 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496121.213091360 DEV_SLEEP_TIME dev:8 wake:000496121.279751190
+ 496121.213092643 SLEEP sleep:000000000.020991366 longest_wake:000158140
+ 496121.234340660 WAKE num_fds:0
+ 496121.234364265 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496121.234393978 FILL_AUDIO dev:8 hw_level:2192
+ 496121.234398053 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.234398418 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.234399290 WRITE_STREAMS_MIXED write_limit:0
+ 496121.234401912 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496121.234408692 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496121.234409113 DEV_SLEEP_TIME dev:8 wake:000496121.280069555
+ 496121.234410491 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496121.234595734 WAKE num_fds:1
+ 496121.234631867 FILL_AUDIO dev:8 hw_level:2192
+ 496121.234634709 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.234636328 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.234640613 DEV_STREAM_MIX written:1024 read:1024
+ 496121.234641319 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.234643008 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496121.234644276 STREAM_SLEEP_TIME id:140000 wake:000496121.255409223
+ 496121.234650119 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496121.234650540 DEV_SLEEP_TIME dev:8 wake:000496121.301643780
+ 496121.234651838 SLEEP sleep:000000000.020765443 longest_wake:000158140
+ 496121.255666578 WAKE num_fds:0
+ 496121.255690188 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496121.255720508 FILL_AUDIO dev:8 hw_level:2208
+ 496121.255724261 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.255724632 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.255725509 WRITE_STREAMS_MIXED write_limit:0
+ 496121.255728055 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496121.255734826 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496121.255735242 DEV_SLEEP_TIME dev:8 wake:000496121.301729027
+ 496121.255736630 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496121.255919587 WAKE num_fds:1
+ 496121.255961218 FILL_AUDIO dev:8 hw_level:2160
+ 496121.255964646 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.255966716 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.255972560 DEV_STREAM_MIX written:1024 read:1024
+ 496121.255973502 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.255975742 FILL_AUDIO_DONE hw_level:2160 total_written:1024 min_cb_level:1024
+ 496121.255977441 STREAM_SLEEP_TIME id:140000 wake:000496121.276742556
+ 496121.256005751 SET_DEV_WAKE dev:8 hw_level:3184 sleep:3184
+ 496121.256006408 DEV_SLEEP_TIME dev:8 wake:000496121.322310077
+ 496121.256008177 SLEEP sleep:000000000.020765812 longest_wake:000158140
+ 496121.276833494 WAKE num_fds:0
+ 496121.276877902 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496121.276909971 FILL_AUDIO dev:8 hw_level:2176
+ 496121.276917979 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.276918350 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.276919974 WRITE_STREAMS_MIXED write_limit:0
+ 496121.276924930 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496121.276932894 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496121.276933325 DEV_SLEEP_TIME dev:8 wake:000496121.322259882
+ 496121.276934914 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496121.277102220 WAKE num_fds:1
+ 496121.277141049 FILL_AUDIO dev:8 hw_level:2176
+ 496121.277146141 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.277147930 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.277154756 DEV_STREAM_MIX written:1024 read:1024
+ 496121.277155468 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.277157577 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496121.277159051 STREAM_SLEEP_TIME id:140000 wake:000496121.298075889
+ 496121.277165325 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496121.277165756 DEV_SLEEP_TIME dev:8 wake:000496121.343825146
+ 496121.277167140 SLEEP sleep:000000000.020917409 longest_wake:000158140
+ 496121.298348914 WAKE num_fds:0
+ 496121.298371636 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496121.298402057 FILL_AUDIO dev:8 hw_level:2192
+ 496121.298407078 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.298407444 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.298408321 WRITE_STREAMS_MIXED write_limit:0
+ 496121.298410827 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496121.298417593 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496121.298418013 DEV_SLEEP_TIME dev:8 wake:000496121.344078475
+ 496121.298419487 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496121.298605947 WAKE num_fds:1
+ 496121.298642421 FILL_AUDIO dev:8 hw_level:2192
+ 496121.298645453 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.298646977 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.298651342 DEV_STREAM_MIX written:1024 read:1024
+ 496121.298652024 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.298653682 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496121.298654975 STREAM_SLEEP_TIME id:140000 wake:000496121.319409222
+ 496121.298660674 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496121.298661120 DEV_SLEEP_TIME dev:8 wake:000496121.365654424
+ 496121.298662398 SLEEP sleep:000000000.020754798 longest_wake:000158140
+ 496121.319517158 WAKE num_fds:0
+ 496121.319540282 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496121.319569664 FILL_AUDIO dev:8 hw_level:2208
+ 496121.319573483 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.319573844 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.319574716 WRITE_STREAMS_MIXED write_limit:0
+ 496121.319577397 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496121.319584288 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496121.319584709 DEV_SLEEP_TIME dev:8 wake:000496121.365578450
+ 496121.319586107 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496121.319772252 WAKE num_fds:1
+ 496121.319806747 FILL_AUDIO dev:8 hw_level:2208
+ 496121.319809608 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.319811162 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.319817070 DEV_STREAM_MIX written:1024 read:1024
+ 496121.319817782 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.319819521 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496121.319820794 STREAM_SLEEP_TIME id:140000 wake:000496121.340742555
+ 496121.319826878 SET_DEV_WAKE dev:8 hw_level:3184 sleep:3184
+ 496121.319827319 DEV_SLEEP_TIME dev:8 wake:000496121.386153636
+ 496121.319828577 SLEEP sleep:000000000.020922252 longest_wake:000158140
+ 496121.340779013 WAKE num_fds:0
+ 496121.340801190 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496121.340820529 FILL_AUDIO dev:8 hw_level:2224
+ 496121.340824208 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.340824574 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.340825431 WRITE_STREAMS_MIXED write_limit:0
+ 496121.340827766 FILL_AUDIO_DONE hw_level:2224 total_written:0 min_cb_level:1024
+ 496121.340835128 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496121.340835564 DEV_SLEEP_TIME dev:8 wake:000496121.386162156
+ 496121.340837022 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496121.340934888 WAKE num_fds:1
+ 496121.340962677 FILL_AUDIO dev:8 hw_level:2176
+ 496121.340965148 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.340967083 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.340972921 DEV_STREAM_MIX written:1024 read:1024
+ 496121.340973838 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.340975302 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496121.340976885 STREAM_SLEEP_TIME id:140000 wake:000496121.362075888
+ 496121.340983536 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496121.340984062 DEV_SLEEP_TIME dev:8 wake:000496121.407642910
+ 496121.340985375 SLEEP sleep:000000000.021099644 longest_wake:000158140
+ 496121.362354958 WAKE num_fds:0
+ 496121.362378873 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496121.362408697 FILL_AUDIO dev:8 hw_level:2192
+ 496121.362412716 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.362413087 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.362413959 WRITE_STREAMS_MIXED write_limit:0
+ 496121.362416470 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496121.362423406 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496121.362423822 DEV_SLEEP_TIME dev:8 wake:000496121.408084143
+ 496121.362425310 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496121.362609405 WAKE num_fds:1
+ 496121.362644947 FILL_AUDIO dev:8 hw_level:2192
+ 496121.362647794 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.362649352 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.362655296 DEV_STREAM_MIX written:1024 read:1024
+ 496121.362656003 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.362657697 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496121.362658959 STREAM_SLEEP_TIME id:140000 wake:000496121.383409221
+ 496121.362664853 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496121.362665284 DEV_SLEEP_TIME dev:8 wake:000496121.429658468
+ 496121.362666582 SLEEP sleep:000000000.020750753 longest_wake:000158140
+ 496121.383661311 WAKE num_fds:0
+ 496121.383685507 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496121.383715957 FILL_AUDIO dev:8 hw_level:2208
+ 496121.383719771 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.383720152 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.383721169 WRITE_STREAMS_MIXED write_limit:0
+ 496121.383723635 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496121.383730520 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496121.383730941 DEV_SLEEP_TIME dev:8 wake:000496121.429724627
+ 496121.383732590 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496121.383917362 WAKE num_fds:1
+ 496121.383953876 FILL_AUDIO dev:8 hw_level:2160
+ 496121.383956778 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.383958356 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.383964300 DEV_STREAM_MIX written:1024 read:1024
+ 496121.383965012 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.383966590 FILL_AUDIO_DONE hw_level:2160 total_written:1024 min_cb_level:1024
+ 496121.383967858 STREAM_SLEEP_TIME id:140000 wake:000496121.404742554
+ 496121.383973662 SET_DEV_WAKE dev:8 hw_level:3184 sleep:3184
+ 496121.383974088 DEV_SLEEP_TIME dev:8 wake:000496121.450300690
+ 496121.383975396 SLEEP sleep:000000000.020775197 longest_wake:000158140
+ 496121.404849376 WAKE num_fds:0
+ 496121.404872895 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496121.404901997 FILL_AUDIO dev:8 hw_level:2176
+ 496121.404905926 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.404906297 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.404907169 WRITE_STREAMS_MIXED write_limit:0
+ 496121.404909630 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496121.404916471 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496121.404916897 DEV_SLEEP_TIME dev:8 wake:000496121.450243925
+ 496121.404918325 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496121.405102806 WAKE num_fds:1
+ 496121.405139886 FILL_AUDIO dev:8 hw_level:2176
+ 496121.405142743 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.405144241 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.405150476 DEV_STREAM_MIX written:1024 read:1024
+ 496121.405151182 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.405152826 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496121.405154069 STREAM_SLEEP_TIME id:140000 wake:000496121.426075887
+ 496121.405159912 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496121.405160338 DEV_SLEEP_TIME dev:8 wake:000496121.471820239
+ 496121.405161661 SLEEP sleep:000000000.020922314 longest_wake:000158140
+ 496121.426187061 WAKE num_fds:0
+ 496121.426210430 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496121.426240775 FILL_AUDIO dev:8 hw_level:2192
+ 496121.426244468 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.426244844 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.426245716 WRITE_STREAMS_MIXED write_limit:0
+ 496121.426248337 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496121.426255243 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496121.426255674 DEV_SLEEP_TIME dev:8 wake:000496121.471916076
+ 496121.426257068 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496121.426460306 WAKE num_fds:1
+ 496121.426497361 FILL_AUDIO dev:8 hw_level:2192
+ 496121.426500183 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.426501801 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.426507575 DEV_STREAM_MIX written:1024 read:1024
+ 496121.426508286 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.426509890 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496121.426511163 STREAM_SLEEP_TIME id:140000 wake:000496121.447409220
+ 496121.426516901 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496121.426517322 DEV_SLEEP_TIME dev:8 wake:000496121.493510657
+ 496121.426518570 SLEEP sleep:000000000.020898563 longest_wake:000158140
+ 496121.447437098 WAKE num_fds:0
+ 496121.447459314 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496121.447478694 FILL_AUDIO dev:8 hw_level:2208
+ 496121.447482052 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.447482413 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.447483420 WRITE_STREAMS_MIXED write_limit:0
+ 496121.447485916 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496121.447492992 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496121.447493413 DEV_SLEEP_TIME dev:8 wake:000496121.493486988
+ 496121.447494891 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496121.447638448 WAKE num_fds:1
+ 496121.447662433 FILL_AUDIO dev:8 hw_level:2208
+ 496121.447663871 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.447665395 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.447669329 DEV_STREAM_MIX written:1024 read:1024
+ 496121.447669940 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.447670717 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496121.447671765 STREAM_SLEEP_TIME id:140000 wake:000496121.468742553
+ 496121.447677317 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496121.447677698 DEV_SLEEP_TIME dev:8 wake:000496121.515004652
+ 496121.447678721 SLEEP sleep:000000000.021071234 longest_wake:000158140
+ 496121.468810020 WAKE num_fds:0
+ 496121.468833494 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496121.468862756 FILL_AUDIO dev:8 hw_level:2176
+ 496121.468866450 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.468866821 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.468867683 WRITE_STREAMS_MIXED write_limit:0
+ 496121.468870224 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496121.468877119 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496121.468877535 DEV_SLEEP_TIME dev:8 wake:000496121.514204544
+ 496121.468878934 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496121.469065043 WAKE num_fds:1
+ 496121.469101332 FILL_AUDIO dev:8 hw_level:2176
+ 496121.469104214 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.469105782 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.469110273 DEV_STREAM_MIX written:1024 read:1024
+ 496121.469110984 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.469112643 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496121.469113906 STREAM_SLEEP_TIME id:140000 wake:000496121.490075886
+ 496121.469119609 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496121.469120035 DEV_SLEEP_TIME dev:8 wake:000496121.535780076
+ 496121.469121338 SLEEP sleep:000000000.020962476 longest_wake:000158140
+ 496121.490134710 WAKE num_fds:0
+ 496121.490157758 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496121.490176612 FILL_AUDIO dev:8 hw_level:2192
+ 496121.490180090 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.490180455 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.490181328 WRITE_STREAMS_MIXED write_limit:0
+ 496121.490183939 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496121.490191015 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496121.490191441 DEV_SLEEP_TIME dev:8 wake:000496121.535851602
+ 496121.490192819 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496121.490344027 WAKE num_fds:1
+ 496121.490368824 FILL_AUDIO dev:8 hw_level:2192
+ 496121.490369947 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.490371526 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.490375760 DEV_STREAM_MIX written:1024 read:1024
+ 496121.490376372 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.490377103 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496121.490378071 STREAM_SLEEP_TIME id:140000 wake:000496121.511409219
+ 496121.490383679 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496121.490384044 DEV_SLEEP_TIME dev:8 wake:000496121.557377630
+ 496121.490385037 SLEEP sleep:000000000.021031589 longest_wake:000158140
+ 496121.511477967 WAKE num_fds:0
+ 496121.511498976 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496121.511518240 FILL_AUDIO dev:8 hw_level:2208
+ 496121.511521848 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.511522214 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.511523071 WRITE_STREAMS_MIXED write_limit:0
+ 496121.511525662 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496121.511532739 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496121.511533159 DEV_SLEEP_TIME dev:8 wake:000496121.557526780
+ 496121.511534738 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496121.511709978 WAKE num_fds:1
+ 496121.511744327 FILL_AUDIO dev:8 hw_level:2208
+ 496121.511746938 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.511748527 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.511752872 DEV_STREAM_MIX written:1024 read:1024
+ 496121.511753583 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.511755062 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496121.511756370 STREAM_SLEEP_TIME id:140000 wake:000496121.532742552
+ 496121.511762093 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496121.511762519 DEV_SLEEP_TIME dev:8 wake:000496121.579089151
+ 496121.511763777 SLEEP sleep:000000000.020986734 longest_wake:000158140
+ 496121.532787267 WAKE num_fds:0
+ 496121.532806276 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496121.532828853 FILL_AUDIO dev:8 hw_level:2176
+ 496121.532832171 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.532832546 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.532833393 WRITE_STREAMS_MIXED write_limit:0
+ 496121.532835618 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496121.532842209 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496121.532842640 DEV_SLEEP_TIME dev:8 wake:000496121.578169939
+ 496121.532844013 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496121.533004248 WAKE num_fds:1
+ 496121.533031992 FILL_AUDIO dev:8 hw_level:2176
+ 496121.533033139 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.533034768 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.533039339 DEV_STREAM_MIX written:1024 read:1024
+ 496121.533039885 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.533040546 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496121.533041479 STREAM_SLEEP_TIME id:140000 wake:000496121.554075885
+ 496121.533046991 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496121.533047332 DEV_SLEEP_TIME dev:8 wake:000496121.599707689
+ 496121.533048249 SLEEP sleep:000000000.021034862 longest_wake:000158140
+ 496121.554149766 WAKE num_fds:0
+ 496121.554166364 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496121.554188087 FILL_AUDIO dev:8 hw_level:2192
+ 496121.554190863 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.554191054 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.554191551 WRITE_STREAMS_MIXED write_limit:0
+ 496121.554193238 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496121.554198768 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496121.554198995 DEV_SLEEP_TIME dev:8 wake:000496121.599860491
+ 496121.554199801 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496121.554306442 WAKE num_fds:1
+ 496121.554337642 FILL_AUDIO dev:8 hw_level:2192
+ 496121.554339165 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.554340148 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.554342551 DEV_STREAM_MIX written:1024 read:1024
+ 496121.554342938 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.554343832 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496121.554344526 STREAM_SLEEP_TIME id:140000 wake:000496121.575409218
+ 496121.554349427 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496121.554349648 DEV_SLEEP_TIME dev:8 wake:000496121.621344250
+ 496121.554350349 SLEEP sleep:000000000.021064968 longest_wake:000158140
+ 496121.575454337 WAKE num_fds:0
+ 496121.575494936 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496121.575524409 FILL_AUDIO dev:8 hw_level:2208
+ 496121.575533510 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.575533881 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.575535084 WRITE_STREAMS_MIXED write_limit:0
+ 496121.575542005 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496121.575550880 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496121.575551301 DEV_SLEEP_TIME dev:8 wake:000496121.621544054
+ 496121.575553642 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496121.575716452 WAKE num_fds:1
+ 496121.575751844 FILL_AUDIO dev:8 hw_level:2208
+ 496121.575754250 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.575758309 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.575767365 DEV_STREAM_MIX written:1024 read:1024
+ 496121.575768162 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.575770342 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496121.575771555 STREAM_SLEEP_TIME id:140000 wake:000496121.596742551
+ 496121.575778536 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496121.575778881 DEV_SLEEP_TIME dev:8 wake:000496121.643104391
+ 496121.575779819 SLEEP sleep:000000000.020971493 longest_wake:000158140
+ 496121.596772698 WAKE num_fds:0
+ 496121.596791942 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496121.596812365 FILL_AUDIO dev:8 hw_level:2224
+ 496121.596816053 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.596816449 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.596817366 WRITE_STREAMS_MIXED write_limit:0
+ 496121.596819742 FILL_AUDIO_DONE hw_level:2224 total_written:0 min_cb_level:1024
+ 496121.596827289 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496121.596827710 DEV_SLEEP_TIME dev:8 wake:000496121.642154277
+ 496121.596829168 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496121.596956217 WAKE num_fds:1
+ 496121.596983470 FILL_AUDIO dev:8 hw_level:2176
+ 496121.596984933 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.596986747 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.596992150 DEV_STREAM_MIX written:1024 read:1024
+ 496121.596992816 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.596993984 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496121.596995237 STREAM_SLEEP_TIME id:140000 wake:000496121.618075884
+ 496121.597001296 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496121.597001767 DEV_SLEEP_TIME dev:8 wake:000496121.663661412
+ 496121.597003526 SLEEP sleep:000000000.021081138 longest_wake:000158140
+ 496121.618124193 WAKE num_fds:0
+ 496121.618142226 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496121.618162091 FILL_AUDIO dev:8 hw_level:2192
+ 496121.618164259 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.618164430 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.618164887 WRITE_STREAMS_MIXED write_limit:0
+ 496121.618166527 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496121.618172515 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496121.618172712 DEV_SLEEP_TIME dev:8 wake:000496121.663833716
+ 496121.618173602 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496121.618250086 WAKE num_fds:1
+ 496121.618275409 FILL_AUDIO dev:8 hw_level:2192
+ 496121.618277345 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.618278382 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.618282712 DEV_STREAM_MIX written:1024 read:1024
+ 496121.618283080 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.618284169 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496121.618284939 STREAM_SLEEP_TIME id:140000 wake:000496121.639409217
+ 496121.618290559 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496121.618290797 DEV_SLEEP_TIME dev:8 wake:000496121.685284641
+ 496121.618291691 SLEEP sleep:000000000.021124576 longest_wake:000158140
+ 496121.639440385 WAKE num_fds:0
+ 496121.639455997 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496121.639474693 FILL_AUDIO dev:8 hw_level:2208
+ 496121.639476921 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.639477163 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.639477713 WRITE_STREAMS_MIXED write_limit:0
+ 496121.639479088 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496121.639484385 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496121.639484585 DEV_SLEEP_TIME dev:8 wake:000496121.685479618
+ 496121.639485314 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496121.639571021 WAKE num_fds:1
+ 496121.639588187 FILL_AUDIO dev:8 hw_level:2208
+ 496121.639588853 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.639589796 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.639591916 DEV_STREAM_MIX written:1024 read:1024
+ 496121.639592176 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.639592524 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496121.639592994 STREAM_SLEEP_TIME id:140000 wake:000496121.660742550
+ 496121.639597798 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496121.639598010 DEV_SLEEP_TIME dev:8 wake:000496121.706926113
+ 496121.639598591 SLEEP sleep:000000000.021149770 longest_wake:000158140
+ 496121.660777354 WAKE num_fds:0
+ 496121.660803529 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496121.660831248 FILL_AUDIO dev:8 hw_level:2176
+ 496121.660837232 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.660837603 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.660838725 WRITE_STREAMS_MIXED write_limit:0
+ 496121.660842188 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496121.660849174 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496121.660849605 DEV_SLEEP_TIME dev:8 wake:000496121.706176328
+ 496121.660851059 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496121.660972434 WAKE num_fds:1
+ 496121.660998349 FILL_AUDIO dev:8 hw_level:2176
+ 496121.661000228 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.661001912 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.661007961 DEV_STREAM_MIX written:1024 read:1024
+ 496121.661008622 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.661009860 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496121.661011399 STREAM_SLEEP_TIME id:140000 wake:000496121.682075883
+ 496121.661017543 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496121.661017944 DEV_SLEEP_TIME dev:8 wake:000496121.727677223
+ 496121.661018966 SLEEP sleep:000000000.021065326 longest_wake:000158140
+ 496121.682107792 WAKE num_fds:0
+ 496121.682128911 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496121.682148682 FILL_AUDIO dev:8 hw_level:2192
+ 496121.682152465 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.682152846 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.682153863 WRITE_STREAMS_MIXED write_limit:0
+ 496121.682156525 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496121.682164117 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496121.682164533 DEV_SLEEP_TIME dev:8 wake:000496121.727824328
+ 496121.682166262 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496121.682264925 WAKE num_fds:1
+ 496121.682287552 FILL_AUDIO dev:8 hw_level:2192
+ 496121.682289416 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.682291381 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.682297630 DEV_STREAM_MIX written:1024 read:1024
+ 496121.682298352 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.682299835 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496121.682301093 STREAM_SLEEP_TIME id:140000 wake:000496121.703409216
+ 496121.682306160 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496121.682306676 DEV_SLEEP_TIME dev:8 wake:000496121.749300442
+ 496121.682308029 SLEEP sleep:000000000.021108774 longest_wake:000158140
+ 496121.703497071 WAKE num_fds:0
+ 496121.703520750 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496121.703547256 FILL_AUDIO dev:8 hw_level:2208
+ 496121.703551261 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.703551837 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.703552945 WRITE_STREAMS_MIXED write_limit:0
+ 496121.703555796 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496121.703563900 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496121.703564391 DEV_SLEEP_TIME dev:8 wake:000496121.749557079
+ 496121.703566491 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496121.703744086 WAKE num_fds:1
+ 496121.703780810 FILL_AUDIO dev:8 hw_level:2208
+ 496121.703783762 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.703785371 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.703791515 DEV_STREAM_MIX written:1024 read:1024
+ 496121.703792207 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.703793886 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496121.703795229 STREAM_SLEEP_TIME id:140000 wake:000496121.724742549
+ 496121.703801448 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496121.703801869 DEV_SLEEP_TIME dev:8 wake:000496121.771128005
+ 496121.703803137 SLEEP sleep:000000000.020947877 longest_wake:000158140
+ 496121.724787767 WAKE num_fds:0
+ 496121.724808766 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496121.724832015 FILL_AUDIO dev:8 hw_level:2176
+ 496121.724835688 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.724836099 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.724837081 WRITE_STREAMS_MIXED write_limit:0
+ 496121.724839888 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496121.724847761 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496121.724848197 DEV_SLEEP_TIME dev:8 wake:000496121.770174283
+ 496121.724849776 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496121.724991743 WAKE num_fds:1
+ 496121.725028934 FILL_AUDIO dev:8 hw_level:2176
+ 496121.725031786 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.725033399 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.725039383 DEV_STREAM_MIX written:1024 read:1024
+ 496121.725040095 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.725041663 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496121.725042986 STREAM_SLEEP_TIME id:140000 wake:000496121.746075882
+ 496121.725049030 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496121.725049456 DEV_SLEEP_TIME dev:8 wake:000496121.791709096
+ 496121.725050830 SLEEP sleep:000000000.021033452 longest_wake:000158140
+ 496121.746183507 WAKE num_fds:0
+ 496121.746206440 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496121.746236283 FILL_AUDIO dev:8 hw_level:2192
+ 496121.746239977 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.746240348 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.746241220 WRITE_STREAMS_MIXED write_limit:0
+ 496121.746243841 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496121.746250787 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496121.746251208 DEV_SLEEP_TIME dev:8 wake:000496121.791911514
+ 496121.746252752 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496121.746440178 WAKE num_fds:1
+ 496121.746474552 FILL_AUDIO dev:8 hw_level:2192
+ 496121.746477209 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.746478887 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.746484646 DEV_STREAM_MIX written:1024 read:1024
+ 496121.746485362 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.746487167 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496121.746488465 STREAM_SLEEP_TIME id:140000 wake:000496121.767409215
+ 496121.746494258 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496121.746494684 DEV_SLEEP_TIME dev:8 wake:000496121.813487968
+ 496121.746495987 SLEEP sleep:000000000.020921247 longest_wake:000158140
+ 496121.767517668 WAKE num_fds:0
+ 496121.767540631 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496121.767570465 FILL_AUDIO dev:8 hw_level:2208
+ 496121.767574359 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.767574730 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.767575607 WRITE_STREAMS_MIXED write_limit:0
+ 496121.767578077 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496121.767584953 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496121.767585374 DEV_SLEEP_TIME dev:8 wake:000496121.813579080
+ 496121.767586833 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496121.767774586 WAKE num_fds:1
+ 496121.767808720 FILL_AUDIO dev:8 hw_level:2208
+ 496121.767811737 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.767813270 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.767819550 DEV_STREAM_MIX written:1024 read:1024
+ 496121.767820261 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.767821965 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496121.767823248 STREAM_SLEEP_TIME id:140000 wake:000496121.788742548
+ 496121.767829498 SET_DEV_WAKE dev:8 hw_level:3184 sleep:3184
+ 496121.767829924 DEV_SLEEP_TIME dev:8 wake:000496121.834156085
+ 496121.767831212 SLEEP sleep:000000000.020919796 longest_wake:000158140
+ 496121.788852201 WAKE num_fds:0
+ 496121.788875299 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496121.788905694 FILL_AUDIO dev:8 hw_level:2176
+ 496121.788909388 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.788909764 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.788910641 WRITE_STREAMS_MIXED write_limit:0
+ 496121.788913157 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496121.788919962 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496121.788920378 DEV_SLEEP_TIME dev:8 wake:000496121.834247487
+ 496121.788921847 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496121.789105962 WAKE num_fds:1
+ 496121.789141990 FILL_AUDIO dev:8 hw_level:2176
+ 496121.789144821 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.789146320 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.789152188 DEV_STREAM_MIX written:1024 read:1024
+ 496121.789152905 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.789154589 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496121.789155837 STREAM_SLEEP_TIME id:140000 wake:000496121.810075881
+ 496121.789161580 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496121.789162031 DEV_SLEEP_TIME dev:8 wake:000496121.855822012
+ 496121.789163364 SLEEP sleep:000000000.020920535 longest_wake:000158140
+ 496121.810105924 WAKE num_fds:0
+ 496121.810127257 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496121.810146862 FILL_AUDIO dev:8 hw_level:2192
+ 496121.810150015 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.810150306 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.810151004 WRITE_STREAMS_MIXED write_limit:0
+ 496121.810153450 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496121.810160382 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496121.810160763 DEV_SLEEP_TIME dev:8 wake:000496121.855821025
+ 496121.810162046 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496121.810236321 WAKE num_fds:1
+ 496121.810255739 FILL_AUDIO dev:8 hw_level:2192
+ 496121.810256712 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.810257947 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.810261439 DEV_STREAM_MIX written:1024 read:1024
+ 496121.810261907 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.810262882 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496121.810263721 STREAM_SLEEP_TIME id:140000 wake:000496121.831409214
+ 496121.810269822 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496121.810270232 DEV_SLEEP_TIME dev:8 wake:000496121.877263347
+ 496121.810271515 SLEEP sleep:000000000.021145867 longest_wake:000158140
+ 496121.831434429 WAKE num_fds:0
+ 496121.831453119 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496121.831472022 FILL_AUDIO dev:8 hw_level:2208
+ 496121.831475096 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.831475410 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.831476122 WRITE_STREAMS_MIXED write_limit:0
+ 496121.831478086 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496121.831484565 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496121.831484852 DEV_SLEEP_TIME dev:8 wake:000496121.877478858
+ 496121.831486095 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496121.831591475 WAKE num_fds:1
+ 496121.831622506 FILL_AUDIO dev:8 hw_level:2208
+ 496121.831624705 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.831626162 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.831630221 DEV_STREAM_MIX written:1024 read:1024
+ 496121.831630832 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.831632757 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496121.831633906 STREAM_SLEEP_TIME id:140000 wake:000496121.852742547
+ 496121.831640127 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496121.831640498 DEV_SLEEP_TIME dev:8 wake:000496121.898966785
+ 496121.831641684 SLEEP sleep:000000000.021109095 longest_wake:000158140
+ 496121.852996684 WAKE num_fds:0
+ 496121.853020685 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496121.853051536 FILL_AUDIO dev:8 hw_level:2176
+ 496121.853055385 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.853055745 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.853056617 WRITE_STREAMS_MIXED write_limit:0
+ 496121.853059665 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496121.853066580 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496121.853067001 DEV_SLEEP_TIME dev:8 wake:000496121.898393980
+ 496121.853068390 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496121.853258724 WAKE num_fds:1
+ 496121.853296812 FILL_AUDIO dev:8 hw_level:2176
+ 496121.853300455 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.853302259 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.853307968 DEV_STREAM_MIX written:1024 read:1024
+ 496121.853308880 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.853311250 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496121.853312864 STREAM_SLEEP_TIME id:140000 wake:000496121.874075880
+ 496121.853319424 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496121.853319925 DEV_SLEEP_TIME dev:8 wake:000496121.919978883
+ 496121.853321699 SLEEP sleep:000000000.020763663 longest_wake:000158140
+ 496121.874121581 WAKE num_fds:0
+ 496121.874137484 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496121.874154658 FILL_AUDIO dev:8 hw_level:2192
+ 496121.874157144 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.874157263 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.874157596 WRITE_STREAMS_MIXED write_limit:0
+ 496121.874159259 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496121.874164831 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496121.874164963 DEV_SLEEP_TIME dev:8 wake:000496121.919826370
+ 496121.874165593 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496121.874230568 WAKE num_fds:1
+ 496121.874249311 FILL_AUDIO dev:8 hw_level:2192
+ 496121.874250329 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.874251172 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.874253846 DEV_STREAM_MIX written:1024 read:1024
+ 496121.874254168 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.874254879 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496121.874255437 STREAM_SLEEP_TIME id:140000 wake:000496121.895409213
+ 496121.874260087 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496121.874260219 DEV_SLEEP_TIME dev:8 wake:000496121.941255230
+ 496121.874260704 SLEEP sleep:000000000.021153983 longest_wake:000158140
+ 496121.895621418 WAKE num_fds:0
+ 496121.895637180 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496121.895653809 FILL_AUDIO dev:8 hw_level:2208
+ 496121.895655732 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.895655866 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.895656225 WRITE_STREAMS_MIXED write_limit:0
+ 496121.895657665 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496121.895662759 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496121.895662914 DEV_SLEEP_TIME dev:8 wake:000496121.941658210
+ 496121.895663513 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496121.895745235 WAKE num_fds:1
+ 496121.895762578 FILL_AUDIO dev:8 hw_level:2208
+ 496121.895763511 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.895764286 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.895767203 DEV_STREAM_MIX written:1024 read:1024
+ 496121.895767568 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.895768193 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496121.895768676 STREAM_SLEEP_TIME id:140000 wake:000496121.916742546
+ 496121.895773163 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496121.895773318 DEV_SLEEP_TIME dev:8 wake:000496121.963101805
+ 496121.895773786 SLEEP sleep:000000000.020974074 longest_wake:000158140
+ 496121.916816246 WAKE num_fds:0
+ 496121.916829844 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496121.916847665 FILL_AUDIO dev:8 hw_level:2176
+ 496121.916849561 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.916849697 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.916850045 WRITE_STREAMS_MIXED write_limit:0
+ 496121.916851345 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496121.916856369 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496121.916856526 DEV_SLEEP_TIME dev:8 wake:000496121.962185175
+ 496121.916857127 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496121.916936358 WAKE num_fds:1
+ 496121.916953777 FILL_AUDIO dev:8 hw_level:2176
+ 496121.916954713 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.916955421 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.916957165 DEV_STREAM_MIX written:1024 read:1024
+ 496121.916957412 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.916957966 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496121.916958446 STREAM_SLEEP_TIME id:140000 wake:000496121.938075879
+ 496121.916962996 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496121.916963151 DEV_SLEEP_TIME dev:8 wake:000496121.983624913
+ 496121.916963634 SLEEP sleep:000000000.021117632 longest_wake:000158140
+ 496121.938161250 WAKE num_fds:0
+ 496121.938185050 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496121.938214142 FILL_AUDIO dev:8 hw_level:2192
+ 496121.938217785 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.938218156 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.938219033 WRITE_STREAMS_MIXED write_limit:0
+ 496121.938221504 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496121.938228400 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496121.938228816 DEV_SLEEP_TIME dev:8 wake:000496121.983889147
+ 496121.938230174 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496121.938435592 WAKE num_fds:1
+ 496121.938472612 FILL_AUDIO dev:8 hw_level:2192
+ 496121.938476607 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.938478346 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.938484390 DEV_STREAM_MIX written:1024 read:1024
+ 496121.938485106 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.938486865 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496121.938488118 STREAM_SLEEP_TIME id:140000 wake:000496121.959409212
+ 496121.938493766 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496121.938494132 DEV_SLEEP_TIME dev:8 wake:000496122.005487627
+ 496121.938495390 SLEEP sleep:000000000.020921585 longest_wake:000158140
+ 496121.959493040 WAKE num_fds:0
+ 496121.959515848 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496121.959537678 FILL_AUDIO dev:8 hw_level:2208
+ 496121.959541187 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.959541547 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.959542444 WRITE_STREAMS_MIXED write_limit:0
+ 496121.959544775 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496121.959551766 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496121.959552132 DEV_SLEEP_TIME dev:8 wake:000496122.005545807
+ 496121.959553570 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496121.959722871 WAKE num_fds:1
+ 496121.959757015 FILL_AUDIO dev:8 hw_level:2208
+ 496121.959759616 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.959761390 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.959765625 DEV_STREAM_MIX written:1024 read:1024
+ 496121.959766331 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.959767920 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496121.959769213 STREAM_SLEEP_TIME id:140000 wake:000496121.980742545
+ 496121.959775262 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496121.959775628 DEV_SLEEP_TIME dev:8 wake:000496122.027101990
+ 496121.959776906 SLEEP sleep:000000000.020973888 longest_wake:000158140
+ 496121.980846407 WAKE num_fds:0
+ 496121.980870472 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496121.980900422 FILL_AUDIO dev:8 hw_level:2176
+ 496121.980904105 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496121.980904476 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496121.980905353 WRITE_STREAMS_MIXED write_limit:0
+ 496121.980907799 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496121.980914860 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496121.980915226 DEV_SLEEP_TIME dev:8 wake:000496122.026242354
+ 496121.980916564 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496121.981102759 WAKE num_fds:1
+ 496121.981138190 FILL_AUDIO dev:8 hw_level:2176
+ 496121.981141027 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496121.981142591 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496121.981148414 DEV_STREAM_MIX written:1024 read:1024
+ 496121.981149101 WRITE_STREAMS_MIXED write_limit:1024
+ 496121.981150754 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496121.981152037 STREAM_SLEEP_TIME id:140000 wake:000496122.002075878
+ 496121.981157881 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496121.981158252 DEV_SLEEP_TIME dev:8 wake:000496122.047818162
+ 496121.981159515 SLEEP sleep:000000000.020924382 longest_wake:000158140
+ 496122.002337641 WAKE num_fds:0
+ 496122.002361576 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496122.002391655 FILL_AUDIO dev:8 hw_level:2192
+ 496122.002395404 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496122.002395775 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496122.002396652 WRITE_STREAMS_MIXED write_limit:0
+ 496122.002399092 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496122.002405908 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496122.002406324 DEV_SLEEP_TIME dev:8 wake:000496122.048066736
+ 496122.002407727 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496122.002594263 WAKE num_fds:1
+ 496122.002630126 FILL_AUDIO dev:8 hw_level:2192
+ 496122.002632972 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496122.002634551 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496122.002640490 DEV_STREAM_MIX written:1024 read:1024
+ 496122.002641201 WRITE_STREAMS_MIXED write_limit:1024
+ 496122.002642775 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496122.002644058 STREAM_SLEEP_TIME id:140000 wake:000496122.023409211
+ 496122.002649826 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496122.002650252 DEV_SLEEP_TIME dev:8 wake:000496122.069643567
+ 496122.002651565 SLEEP sleep:000000000.020765644 longest_wake:000158140
+ 496122.023443827 WAKE num_fds:0
+ 496122.023465787 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496122.023484866 FILL_AUDIO dev:8 hw_level:2208
+ 496122.023488024 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496122.023488409 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496122.023489432 WRITE_STREAMS_MIXED write_limit:0
+ 496122.023491757 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496122.023499440 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496122.023499856 DEV_SLEEP_TIME dev:8 wake:000496122.069493326
+ 496122.023501389 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496122.023673351 WAKE num_fds:1
+ 496122.023707245 FILL_AUDIO dev:8 hw_level:2208
+ 496122.023709846 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496122.023711369 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496122.023715639 DEV_STREAM_MIX written:1024 read:1024
+ 496122.023716356 WRITE_STREAMS_MIXED write_limit:1024
+ 496122.023717934 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496122.023719237 STREAM_SLEEP_TIME id:140000 wake:000496122.044742544
+ 496122.023724981 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496122.023725401 DEV_SLEEP_TIME dev:8 wake:000496122.091052019
+ 496122.023726710 SLEEP sleep:000000000.021023858 longest_wake:000158140
+ 496122.044805628 WAKE num_fds:0
+ 496122.044828160 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496122.044852285 FILL_AUDIO dev:8 hw_level:2176
+ 496122.044855738 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496122.044856109 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496122.044857096 WRITE_STREAMS_MIXED write_limit:0
+ 496122.044859537 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496122.044866443 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496122.044866884 DEV_SLEEP_TIME dev:8 wake:000496122.090193902
+ 496122.044868277 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496122.045010320 WAKE num_fds:1
+ 496122.045043877 FILL_AUDIO dev:8 hw_level:2176
+ 496122.045046408 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496122.045048012 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496122.045054056 DEV_STREAM_MIX written:1024 read:1024
+ 496122.045054773 WRITE_STREAMS_MIXED write_limit:1024
+ 496122.045056376 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496122.045057684 STREAM_SLEEP_TIME id:140000 wake:000496122.066075877
+ 496122.045063523 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496122.045063944 DEV_SLEEP_TIME dev:8 wake:000496122.111723799
+ 496122.045065247 SLEEP sleep:000000000.021018744 longest_wake:000158140
+ 496122.066136582 WAKE num_fds:0
+ 496122.066157034 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496122.066179426 FILL_AUDIO dev:8 hw_level:2192
+ 496122.066183501 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496122.066183871 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496122.066184819 WRITE_STREAMS_MIXED write_limit:0
+ 496122.066187029 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496122.066193809 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496122.066194230 DEV_SLEEP_TIME dev:8 wake:000496122.111854797
+ 496122.066195614 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496122.066335856 WAKE num_fds:1
+ 496122.066358799 FILL_AUDIO dev:8 hw_level:2192
+ 496122.066359997 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496122.066361581 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496122.066365510 DEV_STREAM_MIX written:1024 read:1024
+ 496122.066366071 WRITE_STREAMS_MIXED write_limit:1024
+ 496122.066366697 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496122.066367615 STREAM_SLEEP_TIME id:140000 wake:000496122.087409210
+ 496122.066372967 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496122.066373363 DEV_SLEEP_TIME dev:8 wake:000496122.133367189
+ 496122.066374330 SLEEP sleep:000000000.021042021 longest_wake:000158140
+ 496122.087471671 WAKE num_fds:0
+ 496122.087493561 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496122.087517421 FILL_AUDIO dev:8 hw_level:2208
+ 496122.087520754 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496122.087521140 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496122.087521987 WRITE_STREAMS_MIXED write_limit:0
+ 496122.087524713 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496122.087531669 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496122.087532090 DEV_SLEEP_TIME dev:8 wake:000496122.133525760
+ 496122.087533503 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496122.087705320 WAKE num_fds:1
+ 496122.087739699 FILL_AUDIO dev:8 hw_level:2208
+ 496122.087742245 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496122.087743899 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496122.087748465 DEV_STREAM_MIX written:1024 read:1024
+ 496122.087749151 WRITE_STREAMS_MIXED write_limit:1024
+ 496122.087750710 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496122.087752033 STREAM_SLEEP_TIME id:140000 wake:000496122.108742543
+ 496122.087757781 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496122.087758202 DEV_SLEEP_TIME dev:8 wake:000496122.155084819
+ 496122.087759490 SLEEP sleep:000000000.020991057 longest_wake:000158140
+ 496122.108799368 WAKE num_fds:0
+ 496122.108822551 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496122.108869730 FILL_AUDIO dev:8 hw_level:2176
+ 496122.108877628 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496122.108877999 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496122.108881858 WRITE_STREAMS_MIXED write_limit:0
+ 496122.108886479 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496122.108895164 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496122.108895585 DEV_SLEEP_TIME dev:8 wake:000496122.154221646
+ 496122.108897805 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496122.109059453 WAKE num_fds:1
+ 496122.109096789 FILL_AUDIO dev:8 hw_level:2176
+ 496122.109100518 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496122.109105585 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496122.109113262 DEV_STREAM_MIX written:1024 read:1024
+ 496122.109114079 WRITE_STREAMS_MIXED write_limit:1024
+ 496122.109118204 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496122.109119286 STREAM_SLEEP_TIME id:140000 wake:000496122.130075876
+ 496122.109126252 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496122.109126653 DEV_SLEEP_TIME dev:8 wake:000496122.175785456
+ 496122.109127856 SLEEP sleep:000000000.020957086 longest_wake:000158140
+ 496122.130119813 WAKE num_fds:0
+ 496122.130140075 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496122.130163088 FILL_AUDIO dev:8 hw_level:2192
+ 496122.130166421 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496122.130166792 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496122.130167639 WRITE_STREAMS_MIXED write_limit:0
+ 496122.130170480 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496122.130177431 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496122.130177852 DEV_SLEEP_TIME dev:8 wake:000496122.175838254
+ 496122.130179326 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496122.130282258 WAKE num_fds:1
+ 496122.130303026 FILL_AUDIO dev:8 hw_level:2192
+ 496122.130304109 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496122.130305512 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496122.130311125 DEV_STREAM_MIX written:1024 read:1024
+ 496122.130311711 WRITE_STREAMS_MIXED write_limit:1024
+ 496122.130312353 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496122.130313300 STREAM_SLEEP_TIME id:140000 wake:000496122.151409209
+ 496122.130318647 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496122.130319053 DEV_SLEEP_TIME dev:8 wake:000496122.197312804
+ 496122.130320116 SLEEP sleep:000000000.021096405 longest_wake:000158140
+ 496122.151491150 WAKE num_fds:0
+ 496122.151513712 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496122.151539091 FILL_AUDIO dev:8 hw_level:2208
+ 496122.151542780 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496122.151543150 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496122.151544022 WRITE_STREAMS_MIXED write_limit:0
+ 496122.151546684 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496122.151553564 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496122.151553985 DEV_SLEEP_TIME dev:8 wake:000496122.197547651
+ 496122.151555409 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496122.151727611 WAKE num_fds:1
+ 496122.151761519 FILL_AUDIO dev:8 hw_level:2208
+ 496122.151764040 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496122.151765614 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496122.151770169 DEV_STREAM_MIX written:1024 read:1024
+ 496122.151770876 WRITE_STREAMS_MIXED write_limit:1024
+ 496122.151772395 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496122.151773642 STREAM_SLEEP_TIME id:140000 wake:000496122.172742542
+ 496122.151779361 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496122.151779782 DEV_SLEEP_TIME dev:8 wake:000496122.219106479
+ 496122.151781085 SLEEP sleep:000000000.020969396 longest_wake:000158140
+ 496122.172792031 WAKE num_fds:0
+ 496122.172815340 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2224
+ 496122.172845539 FILL_AUDIO dev:8 hw_level:2176
+ 496122.172849508 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496122.172849889 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496122.172850761 WRITE_STREAMS_MIXED write_limit:0
+ 496122.172853217 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496122.172860223 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496122.172860644 DEV_SLEEP_TIME dev:8 wake:000496122.218187577
+ 496122.172862143 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496122.173005714 WAKE num_fds:1
+ 496122.173028998 FILL_AUDIO dev:8 hw_level:2176
+ 496122.173029985 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496122.173031554 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496122.173037182 DEV_STREAM_MIX written:1024 read:1024
+ 496122.173037808 WRITE_STREAMS_MIXED write_limit:1024
+ 496122.173038429 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496122.173039507 STREAM_SLEEP_TIME id:140000 wake:000496122.194075875
+ 496122.173045130 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496122.173045476 DEV_SLEEP_TIME dev:8 wake:000496122.239705692
+ 496122.173046378 SLEEP sleep:000000000.021036849 longest_wake:000158140
+ 496122.194116034 WAKE num_fds:0
+ 496122.194140887 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496122.194162928 FILL_AUDIO dev:8 hw_level:2192
+ 496122.194167077 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496122.194167443 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496122.194168390 WRITE_STREAMS_MIXED write_limit:0
+ 496122.194171227 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496122.194178529 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496122.194178980 DEV_SLEEP_TIME dev:8 wake:000496122.239839035
+ 496122.194181155 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496122.194266111 WAKE num_fds:1
+ 496122.194291179 FILL_AUDIO dev:8 hw_level:2192
+ 496122.194292136 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496122.194294035 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496122.194300290 DEV_STREAM_MIX written:1024 read:1024
+ 496122.194300896 WRITE_STREAMS_MIXED write_limit:1024
+ 496122.194301873 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496122.194302780 STREAM_SLEEP_TIME id:140000 wake:000496122.215409208
+ 496122.194309255 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496122.194310062 DEV_SLEEP_TIME dev:8 wake:000496122.261302294
+ 496122.194311440 SLEEP sleep:000000000.021106914 longest_wake:000158140
+ 496122.215448777 WAKE num_fds:0
+ 496122.215463923 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496122.215476895 FILL_AUDIO dev:8 hw_level:2208
+ 496122.215478751 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496122.215478927 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496122.215479448 WRITE_STREAMS_MIXED write_limit:0
+ 496122.215480831 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496122.215486357 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496122.215486562 DEV_SLEEP_TIME dev:8 wake:000496122.261481539
+ 496122.215487384 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496122.215549002 WAKE num_fds:1
+ 496122.215563098 FILL_AUDIO dev:8 hw_level:2208
+ 496122.215563611 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496122.215565341 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496122.215567658 DEV_STREAM_MIX written:1024 read:1024
+ 496122.215567938 WRITE_STREAMS_MIXED write_limit:1024
+ 496122.215568239 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496122.215568747 STREAM_SLEEP_TIME id:140000 wake:000496122.236742541
+ 496122.215573511 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496122.215573716 DEV_SLEEP_TIME dev:8 wake:000496122.282901820
+ 496122.215574324 SLEEP sleep:000000000.021174054 longest_wake:000158140
+ 496122.236813718 WAKE num_fds:0
+ 496122.236830443 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496122.236848304 FILL_AUDIO dev:8 hw_level:2176
+ 496122.236850592 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496122.236850766 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496122.236851211 WRITE_STREAMS_MIXED write_limit:0
+ 496122.236853866 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496122.236859339 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496122.236859539 DEV_SLEEP_TIME dev:8 wake:000496122.282187883
+ 496122.236860303 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496122.236957758 WAKE num_fds:1
+ 496122.236979930 FILL_AUDIO dev:8 hw_level:2176
+ 496122.236981687 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496122.236982827 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496122.236987199 DEV_STREAM_MIX written:1024 read:1024
+ 496122.236987605 WRITE_STREAMS_MIXED write_limit:1024
+ 496122.236988630 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496122.236989373 STREAM_SLEEP_TIME id:140000 wake:000496122.258075874
+ 496122.236994623 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496122.236994884 DEV_SLEEP_TIME dev:8 wake:000496122.303655730
+ 496122.236995656 SLEEP sleep:000000000.021086810 longest_wake:000158140
+ 496122.258105114 WAKE num_fds:0
+ 496122.258126969 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496122.258149697 FILL_AUDIO dev:8 hw_level:2192
+ 496122.258153501 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496122.258153867 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496122.258154713 WRITE_STREAMS_MIXED write_limit:0
+ 496122.258157274 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496122.258164351 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496122.258164817 DEV_SLEEP_TIME dev:8 wake:000496122.303825028
+ 496122.258166230 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496122.258305507 WAKE num_fds:1
+ 496122.258341639 FILL_AUDIO dev:8 hw_level:2192
+ 496122.258343749 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496122.258345653 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496122.258351321 DEV_STREAM_MIX written:1024 read:1024
+ 496122.258352123 WRITE_STREAMS_MIXED write_limit:1024
+ 496122.258353306 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496122.258354649 STREAM_SLEEP_TIME id:140000 wake:000496122.279409207
+ 496122.258361405 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496122.258361921 DEV_SLEEP_TIME dev:8 wake:000496122.325354028
+ 496122.258363414 SLEEP sleep:000000000.021055179 longest_wake:000158140
+ 496122.279475995 WAKE num_fds:0
+ 496122.279498647 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496122.279522287 FILL_AUDIO dev:8 hw_level:2208
+ 496122.279525795 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496122.279526166 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496122.279527153 WRITE_STREAMS_MIXED write_limit:0
+ 496122.279529684 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496122.279536585 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496122.279537021 DEV_SLEEP_TIME dev:8 wake:000496122.325530666
+ 496122.279538424 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496122.279679094 WAKE num_fds:1
+ 496122.279712721 FILL_AUDIO dev:8 hw_level:2208
+ 496122.279715312 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496122.279717558 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496122.279723471 DEV_STREAM_MIX written:1024 read:1024
+ 496122.279724183 WRITE_STREAMS_MIXED write_limit:1024
+ 496122.279725726 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496122.279727069 STREAM_SLEEP_TIME id:140000 wake:000496122.300742540
+ 496122.279732808 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496122.279733229 DEV_SLEEP_TIME dev:8 wake:000496122.347059836
+ 496122.279734562 SLEEP sleep:000000000.021016037 longest_wake:000158140
+ 496122.300998522 WAKE num_fds:0
+ 496122.301022512 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2176
+ 496122.301051805 FILL_AUDIO dev:8 hw_level:2176
+ 496122.301055604 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496122.301055969 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496122.301056836 WRITE_STREAMS_MIXED write_limit:0
+ 496122.301059287 FILL_AUDIO_DONE hw_level:2176 total_written:0 min_cb_level:1024
+ 496122.301066183 SET_DEV_WAKE dev:8 hw_level:2176 sleep:2176
+ 496122.301066619 DEV_SLEEP_TIME dev:8 wake:000496122.346393648
+ 496122.301068007 SLEEP sleep:000000000.045333333 longest_wake:000158140
+ 496122.301254372 WAKE num_fds:1
+ 496122.301290025 FILL_AUDIO dev:8 hw_level:2176
+ 496122.301292946 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496122.301294495 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496122.301300178 DEV_STREAM_MIX written:1024 read:1024
+ 496122.301300885 WRITE_STREAMS_MIXED write_limit:1024
+ 496122.301302534 FILL_AUDIO_DONE hw_level:2176 total_written:1024 min_cb_level:1024
+ 496122.301303781 STREAM_SLEEP_TIME id:140000 wake:000496122.322075873
+ 496122.301309565 SET_DEV_WAKE dev:8 hw_level:3200 sleep:3200
+ 496122.301309981 DEV_SLEEP_TIME dev:8 wake:000496122.367969956
+ 496122.301311284 SLEEP sleep:000000000.020772583 longest_wake:000158140
+ 496122.322353591 WAKE num_fds:0
+ 496122.322376259 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2192
+ 496122.322417509 FILL_AUDIO dev:8 hw_level:2192
+ 496122.322421443 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496122.322421804 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496122.322422681 WRITE_STREAMS_MIXED write_limit:0
+ 496122.322424981 FILL_AUDIO_DONE hw_level:2192 total_written:0 min_cb_level:1024
+ 496122.322431852 SET_DEV_WAKE dev:8 hw_level:2192 sleep:2192
+ 496122.322432273 DEV_SLEEP_TIME dev:8 wake:000496122.368092619
+ 496122.322433771 SLEEP sleep:000000000.045666666 longest_wake:000158140
+ 496122.322600105 WAKE num_fds:1
+ 496122.322628852 FILL_AUDIO dev:8 hw_level:2192
+ 496122.322631252 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496122.322633001 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496122.322638734 DEV_STREAM_MIX written:1024 read:1024
+ 496122.322639617 WRITE_STREAMS_MIXED write_limit:1024
+ 496122.322641145 FILL_AUDIO_DONE hw_level:2192 total_written:1024 min_cb_level:1024
+ 496122.322642659 STREAM_SLEEP_TIME id:140000 wake:000496122.343409206
+ 496122.322648953 SET_DEV_WAKE dev:8 hw_level:3216 sleep:3216
+ 496122.322649474 DEV_SLEEP_TIME dev:8 wake:000496122.389642037
+ 496122.322651183 SLEEP sleep:000000000.020767169 longest_wake:000158140
+ 496122.343460464 WAKE num_fds:0
+ 496122.343483111 WRITE_STREAMS_FETCH_STREAM id:140000 cbth:1024 delay:2208
+ 496122.343504801 FILL_AUDIO dev:8 hw_level:2208
+ 496122.343508630 WRITE_STREAMS_STREAM id:140000 shm_frames:0 cb_pending:1
+ 496122.343508996 WRITE_STREAMS_MIX write_limit:0 max_offset:0
+ 496122.343510093 WRITE_STREAMS_MIXED write_limit:0
+ 496122.343512629 FILL_AUDIO_DONE hw_level:2208 total_written:0 min_cb_level:1024
+ 496122.343519871 SET_DEV_WAKE dev:8 hw_level:2208 sleep:2208
+ 496122.343520302 DEV_SLEEP_TIME dev:8 wake:000496122.389513687
+ 496122.343521810 SLEEP sleep:000000000.046000000 longest_wake:000158140
+ 496122.343659067 WAKE num_fds:1
+ 496122.343692660 FILL_AUDIO dev:8 hw_level:2208
+ 496122.343695401 WRITE_STREAMS_STREAM id:140000 shm_frames:1024 cb_pending:0
+ 496122.343697170 WRITE_STREAMS_MIX write_limit:1024 max_offset:0
+ 496122.343701300 DEV_STREAM_MIX written:1024 read:1024
+ 496122.343702001 WRITE_STREAMS_MIXED write_limit:1024
+ 496122.343703570 FILL_AUDIO_DONE hw_level:2208 total_written:1024 min_cb_level:1024
+ 496122.343704858 STREAM_SLEEP_TIME id:140000 wake:000496122.364742539
+ 496122.343710616 SET_DEV_WAKE dev:8 hw_level:3232 sleep:3232
+ 496122.343711027 DEV_SLEEP_TIME dev:8 wake:000496122.411037640
+ 496122.343712340 SLEEP sleep:000000000.021038232 longest_wake:000158140
+ 496122.359904900 WAKE num_fds:1
+ 496122.359908867 PB_MSG msg_id:6
+ 496122.359909941 STREAM_REMOVED id:140000
+ 496122.359939533 ODEV_NO_STREAMS dev:8 hw_level:2416 write:0
+ 496122.359944076 SET_DEV_WAKE dev:8 hw_level:2416 sleep:1392
+ 496122.359944442 DEV_SLEEP_TIME dev:8 wake:000496122.388940010
+ 496122.359945062 SLEEP sleep:000000000.029000000 longest_wake:000158140
+ 496122.388959912 WAKE num_fds:0
+ 496122.388982707 ODEV_NO_STREAMS dev:8 hw_level:1024 write:1024
+ 496122.388988021 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.388988288 DEV_SLEEP_TIME dev:8 wake:000496122.410316487
+ 496122.388988949 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.410345296 WAKE num_fds:0
+ 496122.410370857 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.410376456 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.410376727 DEV_SLEEP_TIME dev:8 wake:000496122.431704661
+ 496122.410377434 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.431764840 WAKE num_fds:0
+ 496122.431833959 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.431843690 SET_DEV_WAKE dev:8 hw_level:2000 sleep:976
+ 496122.431844254 DEV_SLEEP_TIME dev:8 wake:000496122.452168375
+ 496122.431845811 SLEEP sleep:000000000.020333333 longest_wake:000158140
+ 496122.452219198 WAKE num_fds:0
+ 496122.452281422 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.452292006 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.452292589 DEV_SLEEP_TIME dev:8 wake:000496122.473616205
+ 496122.452294378 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.473669621 WAKE num_fds:0
+ 496122.473700008 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.473706996 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.473707244 DEV_SLEEP_TIME dev:8 wake:000496122.495033989
+ 496122.473708271 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.495074480 WAKE num_fds:0
+ 496122.495131374 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496122.495146355 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.495147373 DEV_SLEEP_TIME dev:8 wake:000496122.516468874
+ 496122.495150668 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.516543702 WAKE num_fds:0
+ 496122.516620145 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.516634195 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.516634731 DEV_SLEEP_TIME dev:8 wake:000496122.537955861
+ 496122.516638331 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.537993577 WAKE num_fds:0
+ 496122.538015496 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496122.538021060 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.538021339 DEV_SLEEP_TIME dev:8 wake:000496122.559349272
+ 496122.538022084 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.559371175 WAKE num_fds:0
+ 496122.559406369 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.559412084 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.559412308 DEV_SLEEP_TIME dev:8 wake:000496122.580740216
+ 496122.559413309 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.580803483 WAKE num_fds:0
+ 496122.580876491 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496122.580892291 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.580892889 DEV_SLEEP_TIME dev:8 wake:000496122.602212271
+ 496122.580896126 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.602249850 WAKE num_fds:0
+ 496122.602273699 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.602279632 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.602279889 DEV_SLEEP_TIME dev:8 wake:000496122.623607597
+ 496122.602281013 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.623630275 WAKE num_fds:0
+ 496122.623652277 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.623657745 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.623657981 DEV_SLEEP_TIME dev:8 wake:000496122.644985979
+ 496122.623658807 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.645029671 WAKE num_fds:0
+ 496122.645048901 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496122.645052376 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.645052602 DEV_SLEEP_TIME dev:8 wake:000496122.666382558
+ 496122.645053154 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.666591508 WAKE num_fds:0
+ 496122.666610205 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.666613617 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.666613845 DEV_SLEEP_TIME dev:8 wake:000496122.687943859
+ 496122.666614421 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.688006670 WAKE num_fds:0
+ 496122.688026198 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496122.688029632 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.688029858 DEV_SLEEP_TIME dev:8 wake:000496122.709359852
+ 496122.688030443 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.709414448 WAKE num_fds:0
+ 496122.709440781 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.709446781 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.709447196 DEV_SLEEP_TIME dev:8 wake:000496122.730774701
+ 496122.709448436 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.730857004 WAKE num_fds:0
+ 496122.730888497 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496122.730895228 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.730895769 DEV_SLEEP_TIME dev:8 wake:000496122.752222632
+ 496122.730897217 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.752337493 WAKE num_fds:0
+ 496122.752370820 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.752377486 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.752378032 DEV_SLEEP_TIME dev:8 wake:000496122.773704945
+ 496122.752379530 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.773777745 WAKE num_fds:0
+ 496122.773809844 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496122.773816680 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.773817282 DEV_SLEEP_TIME dev:8 wake:000496122.795143924
+ 496122.773818700 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.795180466 WAKE num_fds:0
+ 496122.795213337 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.795220052 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.795220588 DEV_SLEEP_TIME dev:8 wake:000496122.816547467
+ 496122.795222002 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.816655372 WAKE num_fds:0
+ 496122.816688037 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.816694728 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.816695264 DEV_SLEEP_TIME dev:8 wake:000496122.838022122
+ 496122.816696782 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.838272918 WAKE num_fds:0
+ 496122.838308290 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496122.838314910 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.838315456 DEV_SLEEP_TIME dev:8 wake:000496122.859642410
+ 496122.838316890 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.859692416 WAKE num_fds:0
+ 496122.859724926 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.859731943 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.859732479 DEV_SLEEP_TIME dev:8 wake:000496122.881059061
+ 496122.859733932 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.881167273 WAKE num_fds:0
+ 496122.881200094 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496122.881206664 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.881207215 DEV_SLEEP_TIME dev:8 wake:000496122.902534184
+ 496122.881208654 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.902785139 WAKE num_fds:0
+ 496122.902814893 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.902821764 SET_DEV_WAKE dev:8 hw_level:2000 sleep:976
+ 496122.902822315 DEV_SLEEP_TIME dev:8 wake:000496122.923148978
+ 496122.902823743 SLEEP sleep:000000000.020333333 longest_wake:000158140
+ 496122.923196282 WAKE num_fds:0
+ 496122.923229584 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.923236269 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.923236810 DEV_SLEEP_TIME dev:8 wake:000496122.944563663
+ 496122.923238234 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.944615625 WAKE num_fds:0
+ 496122.944650330 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.944657045 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.944657587 DEV_SLEEP_TIME dev:8 wake:000496122.965984470
+ 496122.944659050 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.966082719 WAKE num_fds:0
+ 496122.966116547 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496122.966123342 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.966123884 DEV_SLEEP_TIME dev:8 wake:000496122.987450657
+ 496122.966125332 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496122.987704348 WAKE num_fds:0
+ 496122.987737645 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496122.987744150 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496122.987744631 DEV_SLEEP_TIME dev:8 wake:000496123.009071715
+ 496122.987746020 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.009107139 WAKE num_fds:0
+ 496123.009145457 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496123.009153220 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.009153892 DEV_SLEEP_TIME dev:8 wake:000496123.030479863
+ 496123.009155841 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.030584265 WAKE num_fds:0
+ 496123.030618043 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496123.030624739 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.030625275 DEV_SLEEP_TIME dev:8 wake:000496123.051952138
+ 496123.030626703 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.052094028 WAKE num_fds:0
+ 496123.052127385 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496123.052134070 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.052134606 DEV_SLEEP_TIME dev:8 wake:000496123.073461509
+ 496123.052136040 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.073511516 WAKE num_fds:0
+ 496123.073544643 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496123.073551248 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.073551789 DEV_SLEEP_TIME dev:8 wake:000496123.094878732
+ 496123.073553217 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.094926450 WAKE num_fds:0
+ 496123.094961846 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496123.094968617 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.094969168 DEV_SLEEP_TIME dev:8 wake:000496123.116295946
+ 496123.094970737 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.116406874 WAKE num_fds:0
+ 496123.116439890 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496123.116446710 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.116447252 DEV_SLEEP_TIME dev:8 wake:000496123.137774020
+ 496123.116448730 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.137883404 WAKE num_fds:0
+ 496123.137916390 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496123.137923126 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.137923672 DEV_SLEEP_TIME dev:8 wake:000496123.159250490
+ 496123.137925111 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.159287924 WAKE num_fds:0
+ 496123.159335483 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496123.159343406 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.159344138 DEV_SLEEP_TIME dev:8 wake:000496123.180669833
+ 496123.159346042 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.180704931 WAKE num_fds:0
+ 496123.180736895 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496123.180743866 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.180744402 DEV_SLEEP_TIME dev:8 wake:000496123.202071140
+ 496123.180745821 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.202319259 WAKE num_fds:0
+ 496123.202364484 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496123.202371294 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.202371830 DEV_SLEEP_TIME dev:8 wake:000496123.223698598
+ 496123.202373279 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.223730915 WAKE num_fds:0
+ 496123.223763370 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496123.223770086 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.223770622 DEV_SLEEP_TIME dev:8 wake:000496123.245097470
+ 496123.223772065 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.245148129 WAKE num_fds:0
+ 496123.245184318 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496123.245190958 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.245191494 DEV_SLEEP_TIME dev:8 wake:000496123.266518437
+ 496123.245192887 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.266776098 WAKE num_fds:0
+ 496123.266809636 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496123.266817093 SET_DEV_WAKE dev:8 hw_level:2000 sleep:976
+ 496123.266817634 DEV_SLEEP_TIME dev:8 wake:000496123.287143786
+ 496123.266819078 SLEEP sleep:000000000.020333333 longest_wake:000158140
+ 496123.287397671 WAKE num_fds:0
+ 496123.287432130 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496123.287438836 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.287439367 DEV_SLEEP_TIME dev:8 wake:000496123.308766250
+ 496123.287440785 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.308870132 WAKE num_fds:0
+ 496123.308903564 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496123.308910340 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.308910881 DEV_SLEEP_TIME dev:8 wake:000496123.330237814
+ 496123.308912360 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.330308465 WAKE num_fds:0
+ 496123.330350260 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496123.330356966 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.330357512 DEV_SLEEP_TIME dev:8 wake:000496123.351684385
+ 496123.330358955 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.351937251 WAKE num_fds:0
+ 496123.351970653 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496123.351977293 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.351977825 DEV_SLEEP_TIME dev:8 wake:000496123.373304743
+ 496123.351979268 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.373411526 WAKE num_fds:0
+ 496123.373445334 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496123.373452009 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.373452550 DEV_SLEEP_TIME dev:8 wake:000496123.394779408
+ 496123.373453984 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.395033698 WAKE num_fds:0
+ 496123.395067641 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496123.395074336 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.395074883 DEV_SLEEP_TIME dev:8 wake:000496123.416401736
+ 496123.395076306 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.416466100 WAKE num_fds:0
+ 496123.416499903 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496123.416506809 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.416507341 DEV_SLEEP_TIME dev:8 wake:000496123.437833978
+ 496123.416508784 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.437920547 WAKE num_fds:0
+ 496123.437948816 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496123.437954655 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.437955070 DEV_SLEEP_TIME dev:8 wake:000496123.459282732
+ 496123.437956175 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.459515811 WAKE num_fds:0
+ 496123.459544842 ODEV_NO_STREAMS dev:8 hw_level:1040 write:1008
+ 496123.459550812 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.459551220 DEV_SLEEP_TIME dev:8 wake:000496123.480878732
+ 496123.459552302 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.481115112 WAKE num_fds:0
+ 496123.481142011 ODEV_NO_STREAMS dev:8 hw_level:992 write:1056
+ 496123.481147923 SET_DEV_WAKE dev:8 hw_level:2048 sleep:1024
+ 496123.481148342 DEV_SLEEP_TIME dev:8 wake:000496123.502475931
+ 496123.481149428 SLEEP sleep:000000000.021333333 longest_wake:000158140
+ 496123.488698368 WAKE num_fds:1
+ 496123.488701940 PB_MSG msg_id:5
diff --git a/scripts/audio_thread_log_viewer/viewer_c3.py b/scripts/audio_thread_log_viewer/viewer_c3.py
new file mode 100755
index 00000000..a6b11be4
--- /dev/null
+++ b/scripts/audio_thread_log_viewer/viewer_c3.py
@@ -0,0 +1,567 @@
+#!/usr/bin/python
+#
+# Copyright 2016 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.
+#
+
+"""Generates an HTML file with plot of buffer level in the audio thread log."""
+
+import argparse
+import collections
+import logging
+import string
+
+page_content = string.Template("""
+<html meta charset="UTF8">
+<head>
+ <!-- Load c3.css -->
+ <link href="https://rawgit.com/masayuki0812/c3/master/c3.css" rel="stylesheet" type="text/css">
+ <!-- Load d3.js and c3.js -->
+ <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
+ <script src="https://rawgit.com/masayuki0812/c3/master/c3.js" charset="utf-8"></script>
+ <style type="text/css">
+ .c3-grid text {
+ fill: grey;
+ }
+ .event_log_box {
+ font-family: 'Courier New', Courier, 'Lucida Sans Typewriter', 'Lucida Typewriter', monospace;
+ font-size: 20px;
+ font-style: normal;
+ font-variant: normal;
+ font-weight: 300;
+ line-height: 26.4px;
+ white-space: pre;
+ height:50%;
+ width:48%;
+ border:1px solid #ccc;
+ overflow:auto;
+ }
+ .checkbox {
+ font-size: 30px;
+ border: 2px;
+ }
+ .device {
+ font-size: 15px;
+ }
+ .stream{
+ font-size: 15px;
+ }
+ .fetch{
+ }
+ .wake{
+ }
+ </style>
+ <script type="text/javascript">
+ draw_chart = function() {
+ var chart = c3.generate({
+ data: {
+ x: 'time',
+ columns: [
+ ['time', $times],
+ ['buffer_level', $buffer_levels],
+ ],
+ type: 'bar',
+ types: {
+ buffer_level: 'line',
+ },
+ },
+ zoom: {
+ enabled: true,
+ },
+
+ grid: {
+ x: {
+ lines: [
+ $grids,
+ ],
+ },
+ },
+
+ axis: {
+ y: {min: 0, max: $max_y},
+ },
+ });
+ };
+
+ logs = `$logs`;
+ put_logs = function () {
+ document.getElementById('logs').innerHTML = logs;
+ };
+
+ set_initial_checkbox_value = function () {
+ document.getElementById('device').checked = true;
+ document.getElementById('stream').checked = true;
+ document.getElementById('fetch').checked = true;
+ document.getElementById('wake').checked = true;
+ }
+
+ window.onload = function() {
+ draw_chart();
+ put_logs();
+ set_initial_checkbox_value();
+ };
+
+ function handleClick(checkbox) {
+ var class_name = checkbox.id;
+ var elements = document.getElementsByClassName(class_name);
+ var i;
+
+ if (checkbox.checked) {
+ display_value = "block";
+ } else {
+ display_value = "none"
+ }
+
+ console.log("change " + class_name + " to " + display_value);
+ for (i = 0; i < elements.length; i++) {
+ elements[i].style.display = display_value;
+ }
+ }
+
+ </script>
+</head>
+
+<body>
+ <div id="chart" style="height:50%; width:100%" ></div>
+ <div style="margin:0 auto"; class="checkbox">
+ <label><input type="checkbox" onclick="handleClick(this);" id="device">Show device removed/added event</label>
+ <label><input type="checkbox" onclick="handleClick(this);" id="stream">Show stream removed/added event</label>
+ <label><input type="checkbox" onclick="handleClick(this);" id="fetch">Show fetch event</label>
+ <label><input type="checkbox" onclick="handleClick(this);" id="wake">Show wake by num_fds=1 event</label>
+ </div>
+ <div class="event_log_box", id="logs", style="float:left;"></div>
+ <textarea class="event_log_box", id="text", style="float:right;"></textarea>
+</body>
+</html>
+""")
+
+
+Tag = collections.namedtuple('Tag', {'time', 'text', 'position', 'class_name'})
+"""
+The tuple for tags shown on the plot on certain time.
+text is the tag to show, position is the tag position, which is one of
+'start', 'middle', 'end', class_name is one of 'device', 'stream', 'fetch',
+and 'wake' which will be their CSS class name.
+"""
+
+class EventData(object):
+ """The base class of an event."""
+ def __init__(self, time, name):
+ """Initializes an EventData.
+
+ @param time: A string for event time.
+ @param name: A string for event name.
+
+ """
+ self.time = time
+ self.name = name
+ self._text = None
+ self._position = None
+ self._class_name = None
+
+ def GetTag(self):
+ """Gets the tag for this event.
+
+ @returns: A Tag object. Returns None if no need to show tag.
+
+ """
+ if self._text:
+ return Tag(
+ time=self.time, text=self._text, position=self._position,
+ class_name=self._class_name)
+ return None
+
+
+class DeviceEvent(EventData):
+ """Class for device event."""
+ def __init__(self, time, name, device):
+ """Initializes a DeviceEvent.
+
+ @param time: A string for event time.
+ @param name: A string for event name.
+ @param device: A string for device index.
+
+ """
+ super(DeviceEvent, self).__init__(time, name)
+ self.device = device
+ self._position = 'start'
+ self._class_name = 'device'
+
+
+class DeviceRemovedEvent(DeviceEvent):
+ """Class for device removed event."""
+ def __init__(self, time, name, device):
+ """Initializes a DeviceRemovedEvent.
+
+ @param time: A string for event time.
+ @param name: A string for event name.
+ @param device: A string for device index.
+
+ """
+ super(DeviceRemovedEvent, self).__init__(time, name, device)
+ self._text = 'Removed Device %s' % self.device
+
+
+class DeviceAddedEvent(DeviceEvent):
+ """Class for device added event."""
+ def __init__(self, time, name, device):
+ """Initializes a DeviceAddedEvent.
+
+ @param time: A string for event time.
+ @param name: A string for event name.
+ @param device: A string for device index.
+
+ """
+ super(DeviceAddedEvent, self).__init__(time, name, device)
+ self._text = 'Added Device %s' % self.device
+
+
+class LevelEvent(DeviceEvent):
+ """Class for device event with buffer level."""
+ def __init__(self, time, name, device, level):
+ """Initializes a LevelEvent.
+
+ @param time: A string for event time.
+ @param name: A string for event name.
+ @param device: A string for device index.
+ @param level: An int for buffer level.
+
+ """
+ super(LevelEvent, self).__init__(time, name, device)
+ self.level = level
+
+
+class StreamEvent(EventData):
+ """Class for event with stream."""
+ def __init__(self, time, name, stream):
+ """Initializes a StreamEvent.
+
+ @param time: A string for event time.
+ @param name: A string for event name.
+ @param stream: A string for stream id.
+
+ """
+ super(StreamEvent, self).__init__(time, name)
+ self.stream = stream
+ self._class_name = 'stream'
+
+
+class FetchStreamEvent(StreamEvent):
+ """Class for stream fetch event."""
+ def __init__(self, time, name, stream):
+ """Initializes a FetchStreamEvent.
+
+ @param time: A string for event time.
+ @param name: A string for event name.
+ @param stream: A string for stream id.
+
+ """
+ super(FetchStreamEvent, self).__init__(time, name, stream)
+ self._text = 'Fetch %s' % self.stream
+ self._position = 'end'
+ self._class_name = 'fetch'
+
+
+class StreamAddedEvent(StreamEvent):
+ """Class for stream added event."""
+ def __init__(self, time, name, stream):
+ """Initializes a StreamAddedEvent.
+
+ @param time: A string for event time.
+ @param name: A string for event name.
+ @param stream: A string for stream id.
+
+ """
+ super(StreamAddedEvent, self).__init__(time, name, stream)
+ self._text = 'Add stream %s' % self.stream
+ self._position = 'middle'
+
+
+class StreamRemovedEvent(StreamEvent):
+ """Class for stream removed event."""
+ def __init__(self, time, name, stream):
+ """Initializes a StreamRemovedEvent.
+
+ @param time: A string for event time.
+ @param name: A string for event name.
+ @param stream: A string for stream id.
+
+ """
+ super(StreamRemovedEvent, self).__init__(time, name, stream)
+ self._text = 'Remove stream %s' % self.stream
+ self._position = 'middle'
+
+
+class WakeEvent(EventData):
+ """Class for wake event."""
+ def __init__(self, time, name, num_fds):
+ """Initializes a WakeEvent.
+
+ @param time: A string for event time.
+ @param name: A string for event name.
+ @param num_fds: A string for number of fd that wakes audio thread up.
+
+ """
+ super(WakeEvent, self).__init__(time, name)
+ self._position = 'middle'
+ self._class_name = 'wake'
+ if num_fds != '0':
+ self._text = 'num_fds %s' % num_fds
+
+
+class C3LogWriter(object):
+ """Class to handle event data and fill an HTML page using c3.js library"""
+ def __init__(self):
+ """Initializes a C3LogWriter."""
+ self.times = []
+ self.buffer_levels = []
+ self.tags = []
+ self.max_y = 0
+
+ def AddEvent(self, event):
+ """Digests an event.
+
+ Add a tag if this event needs to be shown on grid.
+ Add a buffer level data into buffer_levels if this event has buffer
+ level.
+
+ @param event: An EventData object.
+
+ """
+ tag = event.GetTag()
+ if tag:
+ self.tags.append(tag)
+
+ if isinstance(event, LevelEvent):
+ self.times.append(event.time)
+ self.buffer_levels.append(str(event.level))
+ if event.level > self.max_y:
+ self.max_y = event.level
+ logging.debug('add data for a level event %s: %s',
+ event.time, event.level)
+
+ if (isinstance(event, DeviceAddedEvent) or
+ isinstance(event, DeviceRemovedEvent)):
+ self.times.append(event.time)
+ self.buffer_levels.append('null')
+
+ def _GetGrids(self):
+ """Gets the content to be filled for grids.
+
+ @returns: A str for grid with format:
+ '{value: time1, text: "tag1", position: "position1"},
+ {value: time1, text: "tag1"},...'
+
+ """
+ grids = []
+ for tag in self.tags:
+ content = ('{value: %s, text: "%s", position: "%s", '
+ 'class: "%s"}') % (
+ tag.time, tag.text, tag.position, tag.class_name)
+ grids.append(content)
+ grids_joined = ', '.join(grids)
+ return grids_joined
+
+ def FillPage(self, page_template):
+ """Fills in the page template with content.
+
+ @param page_template: A string for HTML page content with variables
+ to be filled.
+
+ @returns: A string for filled page.
+
+ """
+ times = ', '.join(self.times)
+ buffer_levels = ', '.join(self.buffer_levels)
+ grids = self._GetGrids()
+ filled = page_template.safe_substitute(
+ times=times,
+ buffer_levels=buffer_levels,
+ grids=grids,
+ max_y=str(self.max_y))
+ return filled
+
+
+class EventLogParser(object):
+ """Class for event log parser."""
+ def __init__(self):
+ """Initializes an EventLogParse."""
+ self.parsed_events = []
+
+ def AddEventLog(self, event_log):
+ """Digests a line of event log.
+
+ @param event_log: A line for event log.
+
+ """
+ event = self._ParseOneLine(event_log)
+ if event:
+ self.parsed_events.append(event)
+
+ def GetParsedEvents(self):
+ """Gets the list of parsed events.
+
+ @returns: A list of parsed EventData.
+
+ """
+ return self.parsed_events
+
+ def _ParseOneLine(self, line):
+ """Parses one line of event log.
+
+ Split a line like
+ 169536.504763588 WRITE_STREAMS_FETCH_STREAM id:0 cbth:512 delay:1136
+ into time, name, and props where
+ time = '169536.504763588'
+ name = 'WRITE_STREAMS_FETCH_STREAM'
+ props = {
+ 'id': 0,
+ 'cb_th': 512,
+ 'delay': 1136
+ }
+
+ @param line: A line of event log.
+
+ @returns: A EventData object.
+
+ """
+ line_split = line.split()
+ time, name = line_split[0], line_split[1]
+ logging.debug('time: %s, name: %s', time, name)
+ props = {}
+ for index in xrange(2, len(line_split)):
+ key, value = line_split[index].split(':')
+ props[key] = value
+ logging.debug('props: %s', props)
+ return self._CreateEventData(time, name, props)
+
+ def _CreateEventData(self, time, name, props):
+ """Creates an EventData based on event name.
+
+ @param time: A string for event time.
+ @param name: A string for event name.
+ @param props: A dict for event properties.
+
+ @returns: A EventData object.
+
+ """
+ if name == 'WRITE_STREAMS_FETCH_STREAM':
+ return FetchStreamEvent(time, name, stream=props['id'])
+ if name == 'STREAM_ADDED':
+ return StreamAddedEvent(time, name, stream=props['id'])
+ if name == 'STREAM_REMOVED':
+ return StreamRemovedEvent(time, name, stream=props['id'])
+ if name in ['FILL_AUDIO', 'SET_DEV_WAKE']:
+ return LevelEvent(
+ time, name, device=props['dev'],
+ level=int(props['hw_level']))
+ if name == 'DEV_ADDED':
+ return DeviceAddedEvent(time, name, device=props['dev'])
+ if name == 'DEV_REMOVED':
+ return DeviceRemovedEvent(time, name, device=props['dev'])
+ if name == 'WAKE':
+ return WakeEvent(time, name, num_fds=props['num_fds'])
+ return None
+
+
+class AudioThreadLogParser(object):
+ """Class for audio thread log parser."""
+ def __init__(self, path):
+ """Initializes an AudioThreadLogParser.
+
+ @param path: The audio thread log file path.
+
+ """
+ self.path = path
+ self.content = None
+
+ def Parse(self):
+ """Prases the audio thread logs.
+
+ @returns: A list of event log lines.
+
+ """
+ logging.debug('Using file: %s', self.path)
+ with open(self.path, 'r') as f:
+ self.content = f.read().splitlines()
+
+ # Event logs starting at two lines after 'Audio Thread Event Log'.
+ index_start = self.content.index('Audio Thread Event Log:') + 2
+ # If input is from audio_diagnostic result, use aplay -l line to find
+ # the end of audio thread event logs.
+ try:
+ index_end = self.content.index('=== aplay -l ===')
+ except ValueError:
+ logging.debug(
+ 'Can not find aplay line. This is not from diagnostic')
+ index_end = len(self.content)
+ event_logs = self.content[index_start:index_end]
+ logging.info('Parsed %s log events', len(event_logs))
+ return event_logs
+
+ def FillLogs(self, page_template):
+ """Fills the HTML page template with contents for audio thread logs.
+
+ @param page_template: A string for HTML page content with log variable
+ to be filled.
+
+ @returns: A string for filled page.
+
+ """
+ logs = '\n<br>'.join(self.content)
+ return page_template.substitute(logs=logs)
+
+
+def ParseArgs():
+ """Parses the arguments.
+
+ @returns: The namespace containing parsed arguments.
+
+ """
+ parser = argparse.ArgumentParser(
+ description='Draw time chart from audio thread log',
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+ parser.add_argument('FILE', type=str, help='The audio thread log file')
+ parser.add_argument('-o', type=str, dest='output',
+ default='view.html', help='The output HTML file')
+ parser.add_argument('-d', dest='debug', action='store_true',
+ default=False, help='Show debug message')
+ return parser.parse_args()
+
+
+def Main():
+ """The Main program."""
+ options = ParseArgs()
+ logging.basicConfig(
+ format='%(asctime)s:%(levelname)s:%(message)s',
+ level=logging.DEBUG if options.debug else logging.INFO)
+
+ # Gets lines of event logs.
+ audio_thread_log_parser = AudioThreadLogParser(options.FILE)
+ event_logs = audio_thread_log_parser.Parse()
+
+ # Parses event logs into events.
+ event_log_parser = EventLogParser()
+ for event_log in event_logs:
+ event_log_parser.AddEventLog(event_log)
+ events = event_log_parser.GetParsedEvents()
+
+ # Reads in events in preparation of filling HTML template.
+ c3_writer = C3LogWriter()
+ for event in events:
+ c3_writer.AddEvent(event)
+
+ # Fills in buffer level chart.
+ page_content_with_chart = c3_writer.FillPage(page_content)
+
+ # Fills in audio thread log into text box.
+ page_content_with_chart_and_logs = audio_thread_log_parser.FillLogs(
+ string.Template(page_content_with_chart))
+
+ with open(options.output, 'w') as f:
+ f.write(page_content_with_chart_and_logs)
+
+
+if __name__ == '__main__':
+ Main()
diff --git a/scripts/audio_tuning/frontend/audio.js b/scripts/audio_tuning/frontend/audio.js
index c8f393ae..2476f1a9 100644
--- a/scripts/audio_tuning/frontend/audio.js
+++ b/scripts/audio_tuning/frontend/audio.js
@@ -117,6 +117,58 @@ var analyzer_right; /* The FFT analyzer for right channel */
* The detection result will be stored in this value. When user saves config,
* This value is stored in drc.emphasis_disabled in the config. */
var browser_emphasis_disabled_detection_result;
+/* check_biquad_filter_q detects if the browser implements the lowpass and
+ * highpass biquad filters with the original formula or the new formula from
+ * Audio EQ Cookbook. Chrome changed the filter implementation in R53, see:
+ * https://github.com/GoogleChrome/web-audio-samples/wiki/Detection-of-lowpass-BiquadFilter-implementation
+ * The detection result is saved in this value before the page is initialized.
+ * make_biquad_q() uses this value to compute Q to ensure consistent behavior
+ * on different browser versions.
+ */
+var browser_biquad_filter_uses_audio_cookbook_formula;
+
+/* Check the lowpass implementation and return a promise. */
+function check_biquad_filter_q() {
+ 'use strict';
+ var context = new OfflineAudioContext(1, 128, 48000);
+ var osc = context.createOscillator();
+ var filter1 = context.createBiquadFilter();
+ var filter2 = context.createBiquadFilter();
+ var inverter = context.createGain();
+
+ osc.type = 'sawtooth';
+ osc.frequency.value = 8 * 440;
+ inverter.gain.value = -1;
+ /* each filter should get a different Q value */
+ filter1.Q.value = -1;
+ filter2.Q.value = -20;
+ osc.connect(filter1);
+ osc.connect(filter2);
+ filter1.connect(context.destination);
+ filter2.connect(inverter);
+ inverter.connect(context.destination);
+ osc.start();
+
+ return context.startRendering().then(function (buffer) {
+ return browser_biquad_filter_uses_audio_cookbook_formula =
+ Math.max(...buffer.getChannelData(0)) !== 0;
+ });
+}
+
+/* Return the Q value to be used with the lowpass and highpass biquad filters,
+ * given Q in dB for the original filter formula. If the browser uses the new
+ * formula, conversion is made to simulate the original frequency response
+ * with the new formula.
+ */
+function make_biquad_q(q_db) {
+ if (!browser_biquad_filter_uses_audio_cookbook_formula)
+ return q_db;
+
+ var q_lin = dBToLinear(q_db);
+ var q_new = 1 / Math.sqrt((4 - Math.sqrt(16 - 16 / (q_lin * q_lin))) / 2);
+ q_new = linearToDb(q_new);
+ return q_new;
+}
/* The supported audio element names are different on browsers with different
* versions.*/
@@ -506,7 +558,10 @@ function eq() {
filter.frequency.value = parseFloat(value);
break;
case 'q':
- filter.Q.value = parseFloat(value);
+ value = parseFloat(value);
+ if (filter.type == 'lowpass' || filter.type == 'highpass')
+ value = make_biquad_q(value);
+ filter.Q.value = value;
break;
case 'gain':
filter.gain.value = parseFloat(value);
@@ -837,7 +892,7 @@ function create_lowpass(freq) {
var lp = audioContext.createBiquadFilter();
lp.type = 'lowpass';
lp.frequency.value = freq;
- lp.Q.value = 0;
+ lp.Q.value = make_biquad_q(0);
return lp;
}
@@ -846,7 +901,7 @@ function create_highpass(freq) {
var hp = audioContext.createBiquadFilter();
hp.type = 'highpass';
hp.frequency.value = freq;
- hp.Q.value = 0;
+ hp.Q.value = make_biquad_q(0);
return hp;
}
@@ -1438,12 +1493,17 @@ function global_section(parent) {
window.onload = function() {
fix_audio_elements();
- /* Detects if emphasis is disabled and sets
- * browser_emphasis_disabled_detection_result. */
- get_emphasis_disabled();
- init_config();
- init_audio();
- init_ui();
+ check_biquad_filter_q().then(function (flag) {
+ console.log('Browser biquad filter uses Audio Cookbook formula:', flag);
+ /* Detects if emphasis is disabled and sets
+ * browser_emphasis_disabled_detection_result. */
+ get_emphasis_disabled();
+ init_config();
+ init_audio();
+ init_ui();
+ }).catch(function (reason) {
+ alert('Cannot detect browser biquad filter implementation:', reason);
+ });
};
function init_ui() {
@@ -1760,7 +1820,10 @@ function EqDrawer(canvas, channel) {
}
filter.type = get_config('eq', channel, i, 'type');
filter.frequency.value = centerFreq[i];
- filter.Q.value = q[i];
+ if (filter.type == 'lowpass' || filter.type == 'highpass')
+ filter.Q.value = make_biquad_q(q[i]);
+ else
+ filter.Q.value = q[i];
filter.gain.value = gain[i];
filter.getFrequencyResponse(frequencyHz, magResponse,
phaseResponse);
diff --git a/ucm-config/banjo/byt-max98090/HiFi.conf b/ucm-config/banjo/byt-max98090/HiFi.conf
deleted file mode 100644
index 75577c0a..00000000
--- a/ucm-config/banjo/byt-max98090/HiFi.conf
+++ /dev/null
@@ -1,97 +0,0 @@
-SectionVerb {
- Value {
- OutputDspName "speaker_eq"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Digital EQ 3 Band Switch' off"
- cset "name='Digital EQ 5 Band Switch' off"
- cset "name='Digital EQ 7 Band Switch' off"
- cset "name='Biquad Switch' off"
- cset "name='Filter Mode' Music"
- cset "name='ADC Oversampling Rate' 0"
-
- cset "name='DMIC Mux' DMIC"
- cset "name='MIC2 Mux' IN34"
- cset "name='Right ADC Mixer MIC2 Switch' on"
- cset "name='Left ADC Mixer MIC2 Switch' on"
- cset "name='MIC2 Volume' 20"
- cset "name='Int Mic Switch' on"
-
- cset "name='ADCR Boost Volume' 4"
- cset "name='ADCL Boost Volume' 4"
- cset "name='ADCR Volume' 11"
- cset "name='ADCL Volume' 11"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Speaker Left Mixer Volume' 2"
- cset "name='Speaker Right Mixer Volume' 2"
- cset "name='Record Path DC Blocking' on"
- cset "name='Playback Path DC Blocking' on"
-
- cset "name='Headphone Left Switch' on"
- cset "name='Headphone Right Switch' on"
- cset "name='Headphone Switch' off"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
-
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- cset "name='Ext Spk Switch' on"
- ]
-
- DisableSequence [
- ]
-}
-
-SectionDevice."Headphone".0 {
- Value {
- JackName "byt-max98090 Headphone Jack"
- OutputDspName ""
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Speaker Left Switch' off"
- cset "name='Speaker Right Switch' off"
- cset "name='Headphone Switch' on"
- cset "name='HP Left Out Switch' on"
- cset "name='HP Right Out Switch' on"
- ]
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
- cset "name='Headphone Switch' off"
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- ]
-}
-
-SectionDevice."Mic".0 {
- Value {
- JackName "byt-max98090 Mic Jack"
- CaptureControl "MIC2"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Int Mic Switch' off"
- cset "name='Headset Mic Switch' on"
- cset "name='DMIC Mux' ADC"
- cset "name='Record Path DC Blocking' on"
- ]
-
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='Headset Mic Switch' off"
- cset "name='Int Mic Switch' on"
- cset "name='DMIC Mux' DMIC"
- cset "name='Record Path DC Blocking' off"
- ]
-}
diff --git a/ucm-config/candy/byt-max98090/HiFi.conf b/ucm-config/candy/byt-max98090/HiFi.conf
deleted file mode 100644
index 75577c0a..00000000
--- a/ucm-config/candy/byt-max98090/HiFi.conf
+++ /dev/null
@@ -1,97 +0,0 @@
-SectionVerb {
- Value {
- OutputDspName "speaker_eq"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Digital EQ 3 Band Switch' off"
- cset "name='Digital EQ 5 Band Switch' off"
- cset "name='Digital EQ 7 Band Switch' off"
- cset "name='Biquad Switch' off"
- cset "name='Filter Mode' Music"
- cset "name='ADC Oversampling Rate' 0"
-
- cset "name='DMIC Mux' DMIC"
- cset "name='MIC2 Mux' IN34"
- cset "name='Right ADC Mixer MIC2 Switch' on"
- cset "name='Left ADC Mixer MIC2 Switch' on"
- cset "name='MIC2 Volume' 20"
- cset "name='Int Mic Switch' on"
-
- cset "name='ADCR Boost Volume' 4"
- cset "name='ADCL Boost Volume' 4"
- cset "name='ADCR Volume' 11"
- cset "name='ADCL Volume' 11"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Speaker Left Mixer Volume' 2"
- cset "name='Speaker Right Mixer Volume' 2"
- cset "name='Record Path DC Blocking' on"
- cset "name='Playback Path DC Blocking' on"
-
- cset "name='Headphone Left Switch' on"
- cset "name='Headphone Right Switch' on"
- cset "name='Headphone Switch' off"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
-
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- cset "name='Ext Spk Switch' on"
- ]
-
- DisableSequence [
- ]
-}
-
-SectionDevice."Headphone".0 {
- Value {
- JackName "byt-max98090 Headphone Jack"
- OutputDspName ""
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Speaker Left Switch' off"
- cset "name='Speaker Right Switch' off"
- cset "name='Headphone Switch' on"
- cset "name='HP Left Out Switch' on"
- cset "name='HP Right Out Switch' on"
- ]
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
- cset "name='Headphone Switch' off"
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- ]
-}
-
-SectionDevice."Mic".0 {
- Value {
- JackName "byt-max98090 Mic Jack"
- CaptureControl "MIC2"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Int Mic Switch' off"
- cset "name='Headset Mic Switch' on"
- cset "name='DMIC Mux' ADC"
- cset "name='Record Path DC Blocking' on"
- ]
-
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='Headset Mic Switch' off"
- cset "name='Int Mic Switch' on"
- cset "name='DMIC Mux' DMIC"
- cset "name='Record Path DC Blocking' off"
- ]
-}
diff --git a/ucm-config/candy/byt-max98090/byt-max98090.conf b/ucm-config/candy/byt-max98090/byt-max98090.conf
deleted file mode 100644
index 43b155fa..00000000
--- a/ucm-config/candy/byt-max98090/byt-max98090.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-Comment "Candy internal card"
-
-SectionUseCase."HiFi" {
- File "HiFi.conf"
- Comment "Default"
-}
diff --git a/ucm-config/chell b/ucm-config/chell
new file mode 120000
index 00000000..1ec1f47c
--- /dev/null
+++ b/ucm-config/chell
@@ -0,0 +1 @@
+glados/ \ No newline at end of file
diff --git a/ucm-config/chell-cheets b/ucm-config/chell-cheets
new file mode 120000
index 00000000..1ec1f47c
--- /dev/null
+++ b/ucm-config/chell-cheets
@@ -0,0 +1 @@
+glados/ \ No newline at end of file
diff --git a/ucm-config/clapper/byt-max98090/HiFi.conf b/ucm-config/clapper/byt-max98090/HiFi.conf
deleted file mode 100644
index e4250506..00000000
--- a/ucm-config/clapper/byt-max98090/HiFi.conf
+++ /dev/null
@@ -1,119 +0,0 @@
-SectionVerb {
- Value {
- OutputDspName "speaker_eq"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Digital EQ 3 Band Switch' off"
- cset "name='Digital EQ 5 Band Switch' off"
- cset "name='Digital EQ 7 Band Switch' off"
- cset "name='Biquad Switch' off"
- cset "name='Filter Mode' Music"
- cset "name='ADC Oversampling Rate' 0"
-
- cset "name='DMIC Mux' DMIC"
- cset "name='MIC2 Mux' IN34"
- cset "name='Right ADC Mixer MIC2 Switch' on"
- cset "name='Left ADC Mixer MIC2 Switch' on"
- cset "name='MIC2 Volume' 20"
- cset "name='Int Mic Switch' on"
-
- cset "name='ADCR Boost Volume' 4"
- cset "name='ADCL Boost Volume' 4"
- cset "name='ADCR Volume' 11"
- cset "name='ADCL Volume' 11"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Speaker Left Mixer Volume' 2"
- cset "name='Speaker Right Mixer Volume' 2"
- cset "name='Record Path DC Blocking' on"
- cset "name='Playback Path DC Blocking' on"
-
- cset "name='Headphone Left Switch' on"
- cset "name='Headphone Right Switch' on"
- cset "name='Headphone Switch' off"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
-
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- cset "name='Ext Spk Switch' on"
- ]
-
- DisableSequence [
- ]
-}
-
-SectionDevice."Headphone".0 {
- Value {
- JackName "byt-max98090 Headphone Jack"
- OutputDspName ""
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Speaker Left Switch' off"
- cset "name='Speaker Right Switch' off"
- cset "name='Headphone Switch' on"
- cset "name='HP Left Out Switch' on"
- cset "name='HP Right Out Switch' on"
- ]
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
- cset "name='Headphone Switch' off"
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- ]
-}
-
-SectionDevice."Mic".0 {
- Value {
- JackName "byt-max98090 Mic Jack"
- CaptureControl "MIC2"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Int Mic Switch' off"
- cset "name='Headset Mic Switch' on"
- cset "name='DMIC Mux' ADC"
- cset "name='Record Path DC Blocking' on"
- ]
-
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='Headset Mic Switch' off"
- cset "name='Int Mic Switch' on"
- cset "name='DMIC Mux' DMIC"
- cset "name='Record Path DC Blocking' off"
- ]
-}
-
-SectionModifier."Speaker Swap Mode".0 {
- Comment "Swap the left and right channels of speaker."
-
- EnableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Left Speaker Mixer Left DAC Switch' off"
- cset "name='Right Speaker Mixer Right DAC Switch' off"
- cset "name='Left Speaker Mixer Right DAC Switch' on"
- cset "name='Right Speaker Mixer Left DAC Switch' on"
- ]
-
- DisableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Left Speaker Mixer Right DAC Switch' off"
- cset "name='Right Speaker Mixer Left DAC Switch' off"
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- ]
-}
diff --git a/ucm-config/clapper/byt-max98090/byt-max98090.conf b/ucm-config/clapper/byt-max98090/byt-max98090.conf
deleted file mode 100644
index 6c8f18c5..00000000
--- a/ucm-config/clapper/byt-max98090/byt-max98090.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-Comment "Clapper internal card"
-
-SectionUseCase."HiFi" {
- File "HiFi.conf"
- Comment "Default"
-}
diff --git a/ucm-config/cranky/byt-max98090/HiFi.conf b/ucm-config/cranky/byt-max98090/HiFi.conf
deleted file mode 100644
index 75577c0a..00000000
--- a/ucm-config/cranky/byt-max98090/HiFi.conf
+++ /dev/null
@@ -1,97 +0,0 @@
-SectionVerb {
- Value {
- OutputDspName "speaker_eq"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Digital EQ 3 Band Switch' off"
- cset "name='Digital EQ 5 Band Switch' off"
- cset "name='Digital EQ 7 Band Switch' off"
- cset "name='Biquad Switch' off"
- cset "name='Filter Mode' Music"
- cset "name='ADC Oversampling Rate' 0"
-
- cset "name='DMIC Mux' DMIC"
- cset "name='MIC2 Mux' IN34"
- cset "name='Right ADC Mixer MIC2 Switch' on"
- cset "name='Left ADC Mixer MIC2 Switch' on"
- cset "name='MIC2 Volume' 20"
- cset "name='Int Mic Switch' on"
-
- cset "name='ADCR Boost Volume' 4"
- cset "name='ADCL Boost Volume' 4"
- cset "name='ADCR Volume' 11"
- cset "name='ADCL Volume' 11"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Speaker Left Mixer Volume' 2"
- cset "name='Speaker Right Mixer Volume' 2"
- cset "name='Record Path DC Blocking' on"
- cset "name='Playback Path DC Blocking' on"
-
- cset "name='Headphone Left Switch' on"
- cset "name='Headphone Right Switch' on"
- cset "name='Headphone Switch' off"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
-
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- cset "name='Ext Spk Switch' on"
- ]
-
- DisableSequence [
- ]
-}
-
-SectionDevice."Headphone".0 {
- Value {
- JackName "byt-max98090 Headphone Jack"
- OutputDspName ""
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Speaker Left Switch' off"
- cset "name='Speaker Right Switch' off"
- cset "name='Headphone Switch' on"
- cset "name='HP Left Out Switch' on"
- cset "name='HP Right Out Switch' on"
- ]
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
- cset "name='Headphone Switch' off"
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- ]
-}
-
-SectionDevice."Mic".0 {
- Value {
- JackName "byt-max98090 Mic Jack"
- CaptureControl "MIC2"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Int Mic Switch' off"
- cset "name='Headset Mic Switch' on"
- cset "name='DMIC Mux' ADC"
- cset "name='Record Path DC Blocking' on"
- ]
-
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='Headset Mic Switch' off"
- cset "name='Int Mic Switch' on"
- cset "name='DMIC Mux' DMIC"
- cset "name='Record Path DC Blocking' off"
- ]
-}
diff --git a/ucm-config/cranky/byt-max98090/byt-max98090.conf b/ucm-config/cranky/byt-max98090/byt-max98090.conf
deleted file mode 100644
index 5fd8d424..00000000
--- a/ucm-config/cranky/byt-max98090/byt-max98090.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-Comment "Cranky internal card"
-
-SectionUseCase."HiFi" {
- File "HiFi.conf"
- Comment "Default"
-}
diff --git a/ucm-config/cyan-cheets b/ucm-config/cyan-cheets
new file mode 120000
index 00000000..d8fcd909
--- /dev/null
+++ b/ucm-config/cyan-cheets
@@ -0,0 +1 @@
+cyan \ No newline at end of file
diff --git a/ucm-config/cyan/chtmax98090/HiFi.conf b/ucm-config/cyan/chtmax98090/HiFi.conf
index a4e53086..4cf52044 100644
--- a/ucm-config/cyan/chtmax98090/HiFi.conf
+++ b/ucm-config/cyan/chtmax98090/HiFi.conf
@@ -111,3 +111,25 @@ SectionDevice."Mic".0 {
cset "name='Record Path DC Blocking' off"
]
}
+
+SectionModifier."Speaker Swap Mode".0 {
+ Comment "Swap the left and right channels of speaker."
+
+ EnableSequence [
+ cdev "hw:chtmax98090"
+
+ cset "name='Left Speaker Mixer Left DAC Switch' off"
+ cset "name='Right Speaker Mixer Right DAC Switch' off"
+ cset "name='Left Speaker Mixer Right DAC Switch' on"
+ cset "name='Right Speaker Mixer Left DAC Switch' on"
+ ]
+
+ DisableSequence [
+ cdev "hw:chtmax98090"
+
+ cset "name='Left Speaker Mixer Right DAC Switch' off"
+ cset "name='Right Speaker Mixer Left DAC Switch' off"
+ cset "name='Left Speaker Mixer Left DAC Switch' on"
+ cset "name='Right Speaker Mixer Right DAC Switch' on"
+ ]
+}
diff --git a/ucm-config/daisy/DAISY-I2S/HiFi.conf b/ucm-config/daisy/DAISY-I2S/HiFi.conf
index 6539d874..54d08fcd 100644
--- a/ucm-config/daisy/DAISY-I2S/HiFi.conf
+++ b/ucm-config/daisy/DAISY-I2S/HiFi.conf
@@ -102,6 +102,7 @@ SectionDevice."Headphone".0 {
EnableSequence [
cdev "hw:DAISYI2S"
cset "name='EQ1 Switch' off"
+ cset "name='Speaker Switch' off"
cset "name='Left Headphone Mixer Left DAC1 Switch' on"
cset "name='Right Headphone Mixer Right DAC1 Switch' on"
]
diff --git a/ucm-config/daisy_skate/DAISY-I2S/HiFi.conf b/ucm-config/daisy_skate/DAISY-I2S/HiFi.conf
index c0de0900..2516a961 100644
--- a/ucm-config/daisy_skate/DAISY-I2S/HiFi.conf
+++ b/ucm-config/daisy_skate/DAISY-I2S/HiFi.conf
@@ -36,15 +36,11 @@ SectionDevice."Headphone".0 {
EnableSequence [
cdev "hw:DAISYI2S"
cset "name='EQ1 Switch' off"
- cset "name='Left HP Mixer Left DAC1 Switch' on"
- cset "name='Right HP Mixer Right DAC1 Switch' on"
]
DisableSequence [
cdev "hw:DAISYI2S"
cset "name='EQ1 Mode' Default"
cset "name='EQ1 Switch' on"
- cset "name='Left SPK Mixer Left DAC1 Switch' on"
- cset "name='Right SPK Mixer Right DAC1 Switch' on"
]
}
diff --git a/ucm-config/daisy_spring/DAISY-I2S/HiFi.conf b/ucm-config/daisy_spring/DAISY-I2S/HiFi.conf
index 079af4f0..85fbc562 100644
--- a/ucm-config/daisy_spring/DAISY-I2S/HiFi.conf
+++ b/ucm-config/daisy_spring/DAISY-I2S/HiFi.conf
@@ -59,14 +59,8 @@ SectionDevice."Headphone".0 {
}
EnableSequence [
- cdev "hw:DAISYI2S"
- cset "name='Speaker Switch' off"
- cset "name='Headphone Switch' on"
]
DisableSequence [
- cdev "hw:DAISYI2S"
- cset "name='Headphone Switch' off"
- cset "name='Speaker Switch' on"
]
}
diff --git a/ucm-config/enguarde/byt-max98090/byt-max98090.conf b/ucm-config/enguarde/byt-max98090/byt-max98090.conf
deleted file mode 100644
index 068e4054..00000000
--- a/ucm-config/enguarde/byt-max98090/byt-max98090.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-Comment "Enguarde internal card"
-
-SectionUseCase."HiFi" {
- File "HiFi.conf"
- Comment "Default"
-}
diff --git a/ucm-config/expresso/byt-max98090/HiFi.conf b/ucm-config/expresso/byt-max98090/HiFi.conf
deleted file mode 100644
index 75577c0a..00000000
--- a/ucm-config/expresso/byt-max98090/HiFi.conf
+++ /dev/null
@@ -1,97 +0,0 @@
-SectionVerb {
- Value {
- OutputDspName "speaker_eq"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Digital EQ 3 Band Switch' off"
- cset "name='Digital EQ 5 Band Switch' off"
- cset "name='Digital EQ 7 Band Switch' off"
- cset "name='Biquad Switch' off"
- cset "name='Filter Mode' Music"
- cset "name='ADC Oversampling Rate' 0"
-
- cset "name='DMIC Mux' DMIC"
- cset "name='MIC2 Mux' IN34"
- cset "name='Right ADC Mixer MIC2 Switch' on"
- cset "name='Left ADC Mixer MIC2 Switch' on"
- cset "name='MIC2 Volume' 20"
- cset "name='Int Mic Switch' on"
-
- cset "name='ADCR Boost Volume' 4"
- cset "name='ADCL Boost Volume' 4"
- cset "name='ADCR Volume' 11"
- cset "name='ADCL Volume' 11"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Speaker Left Mixer Volume' 2"
- cset "name='Speaker Right Mixer Volume' 2"
- cset "name='Record Path DC Blocking' on"
- cset "name='Playback Path DC Blocking' on"
-
- cset "name='Headphone Left Switch' on"
- cset "name='Headphone Right Switch' on"
- cset "name='Headphone Switch' off"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
-
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- cset "name='Ext Spk Switch' on"
- ]
-
- DisableSequence [
- ]
-}
-
-SectionDevice."Headphone".0 {
- Value {
- JackName "byt-max98090 Headphone Jack"
- OutputDspName ""
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Speaker Left Switch' off"
- cset "name='Speaker Right Switch' off"
- cset "name='Headphone Switch' on"
- cset "name='HP Left Out Switch' on"
- cset "name='HP Right Out Switch' on"
- ]
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
- cset "name='Headphone Switch' off"
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- ]
-}
-
-SectionDevice."Mic".0 {
- Value {
- JackName "byt-max98090 Mic Jack"
- CaptureControl "MIC2"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Int Mic Switch' off"
- cset "name='Headset Mic Switch' on"
- cset "name='DMIC Mux' ADC"
- cset "name='Record Path DC Blocking' on"
- ]
-
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='Headset Mic Switch' off"
- cset "name='Int Mic Switch' on"
- cset "name='DMIC Mux' DMIC"
- cset "name='Record Path DC Blocking' off"
- ]
-}
diff --git a/ucm-config/expresso/byt-max98090/byt-max98090.conf b/ucm-config/expresso/byt-max98090/byt-max98090.conf
deleted file mode 100644
index 23bf8dfe..00000000
--- a/ucm-config/expresso/byt-max98090/byt-max98090.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-Comment "Expresso internal card"
-
-SectionUseCase."HiFi" {
- File "HiFi.conf"
- Comment "Default"
-}
diff --git a/ucm-config/glados/sklnau8825adi/HiFi.conf b/ucm-config/glados/sklnau8825adi/HiFi.conf
new file mode 100644
index 00000000..376e704c
--- /dev/null
+++ b/ucm-config/glados/sklnau8825adi/HiFi.conf
@@ -0,0 +1,507 @@
+SectionVerb {
+ Value {
+ OutputDspName "speaker_eq"
+ }
+
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset "name='codec1_out mo media0_in mi Switch' off"
+ cset "name='codec0_out mo media0_in mi Switch' on"
+ cset "name='DAC Oversampling Rate' 128"
+ cset "name='Headset Mic Switch' off"
+ cset "name='codec0_iv_in Switch' 1"
+ cset "name='media0_out mo codec0_in mi Switch' off"
+ cset "name='media0_out mo dmic01_hifi_in mi Switch' on"
+ cset "name='Pin 5 Mux' cvt 2"
+ cset "name='Pin 6 Mux' cvt 3"
+ cset "name='Pin 7 Mux' cvt 4"
+ cset "name='Mic Volume' 255"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionDevice."Internal Mic".0 {
+ Value {
+ MaxSoftwareGain "2000"
+ }
+
+ EnableSequence [
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionDevice."Speaker".0 {
+ Value {
+ CoupledMixers "Left Master,Right Master"
+ }
+
+ EnableSequence [
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionDevice."HDMI1".0 {
+ Value {
+ JackName "HDMI/DP, pcm=4 Jack"
+ OutputDspName ""
+ }
+
+ EnableSequence [
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionDevice."HDMI2".0 {
+ Value {
+ JackName "HDMI/DP, pcm=5 Jack"
+ OutputDspName ""
+ }
+
+ EnableSequence [
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionDevice."Headphone".0 {
+ Value {
+ JackName "sklnau8825adi Headset Jack"
+ OutputDspName ""
+ }
+
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset "name='codec0_iv_in Switch' 0"
+ cset "name='codec0_out mo media0_in mi Switch' off"
+ cset "name='codec1_out mo media0_in mi Switch' on"
+ cset "name='Headphone Jack Switch' on"
+ ]
+
+ DisableSequence [
+ cdev "hw:sklnau8825adi"
+ cset "name='codec0_out mo media0_in mi Switch' on"
+ cset "name='codec1_out mo media0_in mi Switch' off"
+ cset "name='Headphone Jack Switch' off"
+ cset "name='codec0_iv_in Switch' 1"
+ ]
+}
+
+SectionDevice."Mic".0 {
+ Value {
+ JackName "sklnau8825adi Headset Jack"
+ CaptureControl "Mic"
+ }
+
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset "name='Headset Mic Switch' on"
+ cset "name='media0_out mo codec0_in mi Switch' on"
+ cset "name='media0_out mo dmic01_hifi_in mi Switch' off"
+ ]
+
+ DisableSequence [
+ cdev "hw:sklnau8825adi"
+ cset "name='Headset Mic Switch' off"
+ cset "name='media0_out mo codec0_in mi Switch' off"
+ cset "name='media0_out mo dmic01_hifi_in mi Switch' on"
+ ]
+}
+
+SectionModifier."Hotword Model ar_eg".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/ar_eg.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model cmn_cn".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/cmn_hans_cn.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model cmn_tw".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/cmn_hant_tw.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model cs_cz".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/cs_cz.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model da_dk".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/da_dk.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model de_de".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/de_de.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model en_au".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/en_au.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model en_gb".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/en_gb.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model en_ie".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/en_ie.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model en_in".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/en_in.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model en_ph".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/en_ph.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model en_us".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/en_us.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model es_419".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/es_419.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model es_ar".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/es_ar.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model es_es".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/es_es.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model es_mx".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/es_mx.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model es_us".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/es_us.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model fa_ir".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/fa_ir.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model fi_fi".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/fi_fi.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model fil_ph".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/fil_ph.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model fr_fr".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/fr_fr.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model hi_in".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/hi_in.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model hr_hr".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/hr_hr.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model id_id".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/id_id.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model it_it".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/it_it.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model ja_jp".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/ja_jp.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model ko_kr".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/ko_kr.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model ms_my".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/ms_my.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model nb_no".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/nb_no.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model nl_nl".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/nl_nl.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model pl_pl".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/pl_pl.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model pt_br".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/pt_br.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model ro_ro".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/ro_ro.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model ru_ru".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/ru_ru.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model sv_se".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/sv_se.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model th_th".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/th_th.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model tr_tr".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/tr_tr.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model vi_vn".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/vi_vn.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
+
+SectionModifier."Hotword Model yue_hk".0 {
+ EnableSequence [
+ cdev "hw:sklnau8825adi"
+ cset-tlv "name='hwd_in hwd 0 mdl params' /opt/google/skl-hotword-support/yue_hant_hk.hwd-blob"
+ ]
+
+ DisableSequence [
+ ]
+}
diff --git a/ucm-config/banjo/byt-max98090/byt-max98090.conf b/ucm-config/glados/sklnau8825adi/sklnau8825adi.conf
index 95cdd0d0..e1b2a278 100644
--- a/ucm-config/banjo/byt-max98090/byt-max98090.conf
+++ b/ucm-config/glados/sklnau8825adi/sklnau8825adi.conf
@@ -1,4 +1,4 @@
-Comment "Banjo internal card"
+Comment "Glados internal card"
SectionUseCase."HiFi" {
File "HiFi.conf"
diff --git a/ucm-config/glimmer/byt-max98090/HiFi.conf b/ucm-config/glimmer-cheets/byt-max98090/HiFi.conf
index 8f6d77da..8f6d77da 100644
--- a/ucm-config/glimmer/byt-max98090/HiFi.conf
+++ b/ucm-config/glimmer-cheets/byt-max98090/HiFi.conf
diff --git a/ucm-config/glimmer/byt-max98090/byt-max98090.conf b/ucm-config/glimmer-cheets/byt-max98090/byt-max98090.conf
index eec1436b..eec1436b 100644
--- a/ucm-config/glimmer/byt-max98090/byt-max98090.conf
+++ b/ucm-config/glimmer-cheets/byt-max98090/byt-max98090.conf
diff --git a/ucm-config/gnawty/byt-max98090/HiFi.conf b/ucm-config/gnawty/byt-max98090/HiFi.conf
deleted file mode 100644
index 75577c0a..00000000
--- a/ucm-config/gnawty/byt-max98090/HiFi.conf
+++ /dev/null
@@ -1,97 +0,0 @@
-SectionVerb {
- Value {
- OutputDspName "speaker_eq"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Digital EQ 3 Band Switch' off"
- cset "name='Digital EQ 5 Band Switch' off"
- cset "name='Digital EQ 7 Band Switch' off"
- cset "name='Biquad Switch' off"
- cset "name='Filter Mode' Music"
- cset "name='ADC Oversampling Rate' 0"
-
- cset "name='DMIC Mux' DMIC"
- cset "name='MIC2 Mux' IN34"
- cset "name='Right ADC Mixer MIC2 Switch' on"
- cset "name='Left ADC Mixer MIC2 Switch' on"
- cset "name='MIC2 Volume' 20"
- cset "name='Int Mic Switch' on"
-
- cset "name='ADCR Boost Volume' 4"
- cset "name='ADCL Boost Volume' 4"
- cset "name='ADCR Volume' 11"
- cset "name='ADCL Volume' 11"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Speaker Left Mixer Volume' 2"
- cset "name='Speaker Right Mixer Volume' 2"
- cset "name='Record Path DC Blocking' on"
- cset "name='Playback Path DC Blocking' on"
-
- cset "name='Headphone Left Switch' on"
- cset "name='Headphone Right Switch' on"
- cset "name='Headphone Switch' off"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
-
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- cset "name='Ext Spk Switch' on"
- ]
-
- DisableSequence [
- ]
-}
-
-SectionDevice."Headphone".0 {
- Value {
- JackName "byt-max98090 Headphone Jack"
- OutputDspName ""
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Speaker Left Switch' off"
- cset "name='Speaker Right Switch' off"
- cset "name='Headphone Switch' on"
- cset "name='HP Left Out Switch' on"
- cset "name='HP Right Out Switch' on"
- ]
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
- cset "name='Headphone Switch' off"
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- ]
-}
-
-SectionDevice."Mic".0 {
- Value {
- JackName "byt-max98090 Mic Jack"
- CaptureControl "MIC2"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Int Mic Switch' off"
- cset "name='Headset Mic Switch' on"
- cset "name='DMIC Mux' ADC"
- cset "name='Record Path DC Blocking' on"
- ]
-
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='Headset Mic Switch' off"
- cset "name='Int Mic Switch' on"
- cset "name='DMIC Mux' DMIC"
- cset "name='Record Path DC Blocking' off"
- ]
-}
diff --git a/ucm-config/gnawty/byt-max98090/byt-max98090.conf b/ucm-config/gnawty/byt-max98090/byt-max98090.conf
deleted file mode 100644
index bd9087ee..00000000
--- a/ucm-config/gnawty/byt-max98090/byt-max98090.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-Comment "Gnawty internal card"
-
-SectionUseCase."HiFi" {
- File "HiFi.conf"
- Comment "Default"
-}
diff --git a/ucm-config/heli/byt-max98090/HiFi.conf b/ucm-config/heli/byt-max98090/HiFi.conf
deleted file mode 100644
index 75577c0a..00000000
--- a/ucm-config/heli/byt-max98090/HiFi.conf
+++ /dev/null
@@ -1,97 +0,0 @@
-SectionVerb {
- Value {
- OutputDspName "speaker_eq"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Digital EQ 3 Band Switch' off"
- cset "name='Digital EQ 5 Band Switch' off"
- cset "name='Digital EQ 7 Band Switch' off"
- cset "name='Biquad Switch' off"
- cset "name='Filter Mode' Music"
- cset "name='ADC Oversampling Rate' 0"
-
- cset "name='DMIC Mux' DMIC"
- cset "name='MIC2 Mux' IN34"
- cset "name='Right ADC Mixer MIC2 Switch' on"
- cset "name='Left ADC Mixer MIC2 Switch' on"
- cset "name='MIC2 Volume' 20"
- cset "name='Int Mic Switch' on"
-
- cset "name='ADCR Boost Volume' 4"
- cset "name='ADCL Boost Volume' 4"
- cset "name='ADCR Volume' 11"
- cset "name='ADCL Volume' 11"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Speaker Left Mixer Volume' 2"
- cset "name='Speaker Right Mixer Volume' 2"
- cset "name='Record Path DC Blocking' on"
- cset "name='Playback Path DC Blocking' on"
-
- cset "name='Headphone Left Switch' on"
- cset "name='Headphone Right Switch' on"
- cset "name='Headphone Switch' off"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
-
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- cset "name='Ext Spk Switch' on"
- ]
-
- DisableSequence [
- ]
-}
-
-SectionDevice."Headphone".0 {
- Value {
- JackName "byt-max98090 Headphone Jack"
- OutputDspName ""
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Speaker Left Switch' off"
- cset "name='Speaker Right Switch' off"
- cset "name='Headphone Switch' on"
- cset "name='HP Left Out Switch' on"
- cset "name='HP Right Out Switch' on"
- ]
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
- cset "name='Headphone Switch' off"
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- ]
-}
-
-SectionDevice."Mic".0 {
- Value {
- JackName "byt-max98090 Mic Jack"
- CaptureControl "MIC2"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Int Mic Switch' off"
- cset "name='Headset Mic Switch' on"
- cset "name='DMIC Mux' ADC"
- cset "name='Record Path DC Blocking' on"
- ]
-
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='Headset Mic Switch' off"
- cset "name='Int Mic Switch' on"
- cset "name='DMIC Mux' DMIC"
- cset "name='Record Path DC Blocking' off"
- ]
-}
diff --git a/ucm-config/heli/byt-max98090/byt-max98090.conf b/ucm-config/heli/byt-max98090/byt-max98090.conf
deleted file mode 100644
index 83f2a827..00000000
--- a/ucm-config/heli/byt-max98090/byt-max98090.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-Comment "Heli internal card"
-
-SectionUseCase."HiFi" {
- File "HiFi.conf"
- Comment "Default"
-}
diff --git a/ucm-config/kip/byt-max98090/HiFi.conf b/ucm-config/kip/byt-max98090/HiFi.conf
deleted file mode 100644
index 75577c0a..00000000
--- a/ucm-config/kip/byt-max98090/HiFi.conf
+++ /dev/null
@@ -1,97 +0,0 @@
-SectionVerb {
- Value {
- OutputDspName "speaker_eq"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Digital EQ 3 Band Switch' off"
- cset "name='Digital EQ 5 Band Switch' off"
- cset "name='Digital EQ 7 Band Switch' off"
- cset "name='Biquad Switch' off"
- cset "name='Filter Mode' Music"
- cset "name='ADC Oversampling Rate' 0"
-
- cset "name='DMIC Mux' DMIC"
- cset "name='MIC2 Mux' IN34"
- cset "name='Right ADC Mixer MIC2 Switch' on"
- cset "name='Left ADC Mixer MIC2 Switch' on"
- cset "name='MIC2 Volume' 20"
- cset "name='Int Mic Switch' on"
-
- cset "name='ADCR Boost Volume' 4"
- cset "name='ADCL Boost Volume' 4"
- cset "name='ADCR Volume' 11"
- cset "name='ADCL Volume' 11"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Speaker Left Mixer Volume' 2"
- cset "name='Speaker Right Mixer Volume' 2"
- cset "name='Record Path DC Blocking' on"
- cset "name='Playback Path DC Blocking' on"
-
- cset "name='Headphone Left Switch' on"
- cset "name='Headphone Right Switch' on"
- cset "name='Headphone Switch' off"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
-
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- cset "name='Ext Spk Switch' on"
- ]
-
- DisableSequence [
- ]
-}
-
-SectionDevice."Headphone".0 {
- Value {
- JackName "byt-max98090 Headphone Jack"
- OutputDspName ""
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Speaker Left Switch' off"
- cset "name='Speaker Right Switch' off"
- cset "name='Headphone Switch' on"
- cset "name='HP Left Out Switch' on"
- cset "name='HP Right Out Switch' on"
- ]
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
- cset "name='Headphone Switch' off"
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- ]
-}
-
-SectionDevice."Mic".0 {
- Value {
- JackName "byt-max98090 Mic Jack"
- CaptureControl "MIC2"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Int Mic Switch' off"
- cset "name='Headset Mic Switch' on"
- cset "name='DMIC Mux' ADC"
- cset "name='Record Path DC Blocking' on"
- ]
-
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='Headset Mic Switch' off"
- cset "name='Int Mic Switch' on"
- cset "name='DMIC Mux' DMIC"
- cset "name='Record Path DC Blocking' off"
- ]
-}
diff --git a/ucm-config/ninja/byt-max98090/HiFi.conf b/ucm-config/ninja/byt-max98090/HiFi.conf
deleted file mode 100644
index 628b6964..00000000
--- a/ucm-config/ninja/byt-max98090/HiFi.conf
+++ /dev/null
@@ -1,81 +0,0 @@
-SectionVerb {
- Value {
- NoCreateDefaultInputNode "1"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Digital EQ 3 Band Switch' off"
- cset "name='Digital EQ 5 Band Switch' off"
- cset "name='Digital EQ 7 Band Switch' off"
- cset "name='Biquad Switch' off"
- cset "name='Filter Mode' Music"
- cset "name='ADC Oversampling Rate' 0"
-
- cset "name='DMIC Mux' ADC"
- cset "name='MIC2 Mux' IN34"
- cset "name='Right ADC Mixer MIC2 Switch' on"
- cset "name='Left ADC Mixer MIC2 Switch' on"
- cset "name='MIC2 Volume' 20"
- cset "name='Int Mic Switch' on"
-
- cset "name='ADCR Boost Volume' 4"
- cset "name='ADCL Boost Volume' 4"
- cset "name='ADCR Volume' 11"
- cset "name='ADCL Volume' 11"
-
- cset "name='Record Path DC Blocking' on"
- cset "name='Playback Path DC Blocking' on"
-
- cset "name='Headphone Left Switch' on"
- cset "name='Headphone Right Switch' on"
- cset "name='Headphone Switch' off"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
- ]
-
- DisableSequence [
- ]
-}
-
-SectionDevice."Headphone".0 {
- Value {
- JackName "byt-max98090 Headphone Jack"
- OutputDspName ""
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Headphone Switch' on"
- cset "name='HP Left Out Switch' on"
- cset "name='HP Right Out Switch' on"
- ]
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
- cset "name='Headphone Switch' off"
- ]
-}
-
-SectionDevice."Mic".0 {
- Value {
- JackName "byt-max98090 Mic Jack"
- CaptureControl "MIC2"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Int Mic Switch' off"
- cset "name='Headset Mic Switch' on"
- cset "name='DMIC Mux' ADC"
- cset "name='Record Path DC Blocking' on"
- ]
-
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='Headset Mic Switch' off"
- cset "name='Record Path DC Blocking' off"
- ]
-}
diff --git a/ucm-config/ninja/byt-max98090/byt-max98090.conf b/ucm-config/ninja/byt-max98090/byt-max98090.conf
deleted file mode 100644
index 255e87e1..00000000
--- a/ucm-config/ninja/byt-max98090/byt-max98090.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-Comment "Ninja internal card"
-
-SectionUseCase."HiFi" {
- File "HiFi.conf"
- Comment "Default"
-}
diff --git a/ucm-config/orco/byt-max98090/HiFi.conf b/ucm-config/orco/byt-max98090/HiFi.conf
deleted file mode 100644
index 75577c0a..00000000
--- a/ucm-config/orco/byt-max98090/HiFi.conf
+++ /dev/null
@@ -1,97 +0,0 @@
-SectionVerb {
- Value {
- OutputDspName "speaker_eq"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Digital EQ 3 Band Switch' off"
- cset "name='Digital EQ 5 Band Switch' off"
- cset "name='Digital EQ 7 Band Switch' off"
- cset "name='Biquad Switch' off"
- cset "name='Filter Mode' Music"
- cset "name='ADC Oversampling Rate' 0"
-
- cset "name='DMIC Mux' DMIC"
- cset "name='MIC2 Mux' IN34"
- cset "name='Right ADC Mixer MIC2 Switch' on"
- cset "name='Left ADC Mixer MIC2 Switch' on"
- cset "name='MIC2 Volume' 20"
- cset "name='Int Mic Switch' on"
-
- cset "name='ADCR Boost Volume' 4"
- cset "name='ADCL Boost Volume' 4"
- cset "name='ADCR Volume' 11"
- cset "name='ADCL Volume' 11"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Speaker Left Mixer Volume' 2"
- cset "name='Speaker Right Mixer Volume' 2"
- cset "name='Record Path DC Blocking' on"
- cset "name='Playback Path DC Blocking' on"
-
- cset "name='Headphone Left Switch' on"
- cset "name='Headphone Right Switch' on"
- cset "name='Headphone Switch' off"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
-
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- cset "name='Ext Spk Switch' on"
- ]
-
- DisableSequence [
- ]
-}
-
-SectionDevice."Headphone".0 {
- Value {
- JackName "byt-max98090 Headphone Jack"
- OutputDspName ""
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Speaker Left Switch' off"
- cset "name='Speaker Right Switch' off"
- cset "name='Headphone Switch' on"
- cset "name='HP Left Out Switch' on"
- cset "name='HP Right Out Switch' on"
- ]
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
- cset "name='Headphone Switch' off"
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- ]
-}
-
-SectionDevice."Mic".0 {
- Value {
- JackName "byt-max98090 Mic Jack"
- CaptureControl "MIC2"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Int Mic Switch' off"
- cset "name='Headset Mic Switch' on"
- cset "name='DMIC Mux' ADC"
- cset "name='Record Path DC Blocking' on"
- ]
-
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='Headset Mic Switch' off"
- cset "name='Int Mic Switch' on"
- cset "name='DMIC Mux' DMIC"
- cset "name='Record Path DC Blocking' off"
- ]
-}
diff --git a/ucm-config/parry/byt-max98090/HiFi.conf b/ucm-config/parry/byt-max98090/HiFi.conf
deleted file mode 100644
index 75577c0a..00000000
--- a/ucm-config/parry/byt-max98090/HiFi.conf
+++ /dev/null
@@ -1,97 +0,0 @@
-SectionVerb {
- Value {
- OutputDspName "speaker_eq"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Digital EQ 3 Band Switch' off"
- cset "name='Digital EQ 5 Band Switch' off"
- cset "name='Digital EQ 7 Band Switch' off"
- cset "name='Biquad Switch' off"
- cset "name='Filter Mode' Music"
- cset "name='ADC Oversampling Rate' 0"
-
- cset "name='DMIC Mux' DMIC"
- cset "name='MIC2 Mux' IN34"
- cset "name='Right ADC Mixer MIC2 Switch' on"
- cset "name='Left ADC Mixer MIC2 Switch' on"
- cset "name='MIC2 Volume' 20"
- cset "name='Int Mic Switch' on"
-
- cset "name='ADCR Boost Volume' 4"
- cset "name='ADCL Boost Volume' 4"
- cset "name='ADCR Volume' 11"
- cset "name='ADCL Volume' 11"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Speaker Left Mixer Volume' 2"
- cset "name='Speaker Right Mixer Volume' 2"
- cset "name='Record Path DC Blocking' on"
- cset "name='Playback Path DC Blocking' on"
-
- cset "name='Headphone Left Switch' on"
- cset "name='Headphone Right Switch' on"
- cset "name='Headphone Switch' off"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
-
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- cset "name='Ext Spk Switch' on"
- ]
-
- DisableSequence [
- ]
-}
-
-SectionDevice."Headphone".0 {
- Value {
- JackName "byt-max98090 Headphone Jack"
- OutputDspName ""
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Speaker Left Switch' off"
- cset "name='Speaker Right Switch' off"
- cset "name='Headphone Switch' on"
- cset "name='HP Left Out Switch' on"
- cset "name='HP Right Out Switch' on"
- ]
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
- cset "name='Headphone Switch' off"
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- ]
-}
-
-SectionDevice."Mic".0 {
- Value {
- JackName "byt-max98090 Mic Jack"
- CaptureControl "MIC2"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Int Mic Switch' off"
- cset "name='Headset Mic Switch' on"
- cset "name='DMIC Mux' ADC"
- cset "name='Record Path DC Blocking' on"
- ]
-
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='Headset Mic Switch' off"
- cset "name='Int Mic Switch' on"
- cset "name='DMIC Mux' DMIC"
- cset "name='Record Path DC Blocking' off"
- ]
-}
diff --git a/ucm-config/parry/byt-max98090/byt-max98090.conf b/ucm-config/parry/byt-max98090/byt-max98090.conf
deleted file mode 100644
index a14a6c43..00000000
--- a/ucm-config/parry/byt-max98090/byt-max98090.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-Comment "Parry internal card"
-
-SectionUseCase."HiFi" {
- File "HiFi.conf"
- Comment "Default"
-}
diff --git a/ucm-config/quawks/byt-max98090/HiFi.conf b/ucm-config/quawks/byt-max98090/HiFi.conf
deleted file mode 100644
index 75577c0a..00000000
--- a/ucm-config/quawks/byt-max98090/HiFi.conf
+++ /dev/null
@@ -1,97 +0,0 @@
-SectionVerb {
- Value {
- OutputDspName "speaker_eq"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Digital EQ 3 Band Switch' off"
- cset "name='Digital EQ 5 Band Switch' off"
- cset "name='Digital EQ 7 Band Switch' off"
- cset "name='Biquad Switch' off"
- cset "name='Filter Mode' Music"
- cset "name='ADC Oversampling Rate' 0"
-
- cset "name='DMIC Mux' DMIC"
- cset "name='MIC2 Mux' IN34"
- cset "name='Right ADC Mixer MIC2 Switch' on"
- cset "name='Left ADC Mixer MIC2 Switch' on"
- cset "name='MIC2 Volume' 20"
- cset "name='Int Mic Switch' on"
-
- cset "name='ADCR Boost Volume' 4"
- cset "name='ADCL Boost Volume' 4"
- cset "name='ADCR Volume' 11"
- cset "name='ADCL Volume' 11"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Speaker Left Mixer Volume' 2"
- cset "name='Speaker Right Mixer Volume' 2"
- cset "name='Record Path DC Blocking' on"
- cset "name='Playback Path DC Blocking' on"
-
- cset "name='Headphone Left Switch' on"
- cset "name='Headphone Right Switch' on"
- cset "name='Headphone Switch' off"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
-
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- cset "name='Ext Spk Switch' on"
- ]
-
- DisableSequence [
- ]
-}
-
-SectionDevice."Headphone".0 {
- Value {
- JackName "byt-max98090 Headphone Jack"
- OutputDspName ""
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Speaker Left Switch' off"
- cset "name='Speaker Right Switch' off"
- cset "name='Headphone Switch' on"
- cset "name='HP Left Out Switch' on"
- cset "name='HP Right Out Switch' on"
- ]
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
- cset "name='Headphone Switch' off"
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- ]
-}
-
-SectionDevice."Mic".0 {
- Value {
- JackName "byt-max98090 Mic Jack"
- CaptureControl "MIC2"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Int Mic Switch' off"
- cset "name='Headset Mic Switch' on"
- cset "name='DMIC Mux' ADC"
- cset "name='Record Path DC Blocking' on"
- ]
-
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='Headset Mic Switch' off"
- cset "name='Int Mic Switch' on"
- cset "name='DMIC Mux' DMIC"
- cset "name='Record Path DC Blocking' off"
- ]
-}
diff --git a/ucm-config/quawks/byt-max98090/byt-max98090.conf b/ucm-config/quawks/byt-max98090/byt-max98090.conf
deleted file mode 100644
index 1856a8d3..00000000
--- a/ucm-config/quawks/byt-max98090/byt-max98090.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-Comment "Quawks internal card"
-
-SectionUseCase."HiFi" {
- File "HiFi.conf"
- Comment "Default"
-}
diff --git a/ucm-config/rambi/byt-max98090/HiFi.conf b/ucm-config/rambi/byt-max98090/HiFi.conf
deleted file mode 100644
index 75577c0a..00000000
--- a/ucm-config/rambi/byt-max98090/HiFi.conf
+++ /dev/null
@@ -1,97 +0,0 @@
-SectionVerb {
- Value {
- OutputDspName "speaker_eq"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Digital EQ 3 Band Switch' off"
- cset "name='Digital EQ 5 Band Switch' off"
- cset "name='Digital EQ 7 Band Switch' off"
- cset "name='Biquad Switch' off"
- cset "name='Filter Mode' Music"
- cset "name='ADC Oversampling Rate' 0"
-
- cset "name='DMIC Mux' DMIC"
- cset "name='MIC2 Mux' IN34"
- cset "name='Right ADC Mixer MIC2 Switch' on"
- cset "name='Left ADC Mixer MIC2 Switch' on"
- cset "name='MIC2 Volume' 20"
- cset "name='Int Mic Switch' on"
-
- cset "name='ADCR Boost Volume' 4"
- cset "name='ADCL Boost Volume' 4"
- cset "name='ADCR Volume' 11"
- cset "name='ADCL Volume' 11"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Speaker Left Mixer Volume' 2"
- cset "name='Speaker Right Mixer Volume' 2"
- cset "name='Record Path DC Blocking' on"
- cset "name='Playback Path DC Blocking' on"
-
- cset "name='Headphone Left Switch' on"
- cset "name='Headphone Right Switch' on"
- cset "name='Headphone Switch' off"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
-
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- cset "name='Ext Spk Switch' on"
- ]
-
- DisableSequence [
- ]
-}
-
-SectionDevice."Headphone".0 {
- Value {
- JackName "byt-max98090 Headphone Jack"
- OutputDspName ""
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Speaker Left Switch' off"
- cset "name='Speaker Right Switch' off"
- cset "name='Headphone Switch' on"
- cset "name='HP Left Out Switch' on"
- cset "name='HP Right Out Switch' on"
- ]
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
- cset "name='Headphone Switch' off"
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- ]
-}
-
-SectionDevice."Mic".0 {
- Value {
- JackName "byt-max98090 Mic Jack"
- CaptureControl "MIC2"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Int Mic Switch' off"
- cset "name='Headset Mic Switch' on"
- cset "name='DMIC Mux' ADC"
- cset "name='Record Path DC Blocking' on"
- ]
-
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='Headset Mic Switch' off"
- cset "name='Int Mic Switch' on"
- cset "name='DMIC Mux' DMIC"
- cset "name='Record Path DC Blocking' off"
- ]
-}
diff --git a/ucm-config/rambi/byt-max98090/byt-max98090.conf b/ucm-config/rambi/byt-max98090/byt-max98090.conf
deleted file mode 100644
index 915d1551..00000000
--- a/ucm-config/rambi/byt-max98090/byt-max98090.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-Comment "Rambi internal card"
-
-SectionUseCase."HiFi" {
- File "HiFi.conf"
- Comment "Default"
-}
diff --git a/ucm-config/squawks/byt-max98090/HiFi.conf b/ucm-config/squawks/byt-max98090/HiFi.conf
deleted file mode 100644
index 75577c0a..00000000
--- a/ucm-config/squawks/byt-max98090/HiFi.conf
+++ /dev/null
@@ -1,97 +0,0 @@
-SectionVerb {
- Value {
- OutputDspName "speaker_eq"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Digital EQ 3 Band Switch' off"
- cset "name='Digital EQ 5 Band Switch' off"
- cset "name='Digital EQ 7 Band Switch' off"
- cset "name='Biquad Switch' off"
- cset "name='Filter Mode' Music"
- cset "name='ADC Oversampling Rate' 0"
-
- cset "name='DMIC Mux' DMIC"
- cset "name='MIC2 Mux' IN34"
- cset "name='Right ADC Mixer MIC2 Switch' on"
- cset "name='Left ADC Mixer MIC2 Switch' on"
- cset "name='MIC2 Volume' 20"
- cset "name='Int Mic Switch' on"
-
- cset "name='ADCR Boost Volume' 4"
- cset "name='ADCL Boost Volume' 4"
- cset "name='ADCR Volume' 11"
- cset "name='ADCL Volume' 11"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Speaker Left Mixer Volume' 2"
- cset "name='Speaker Right Mixer Volume' 2"
- cset "name='Record Path DC Blocking' on"
- cset "name='Playback Path DC Blocking' on"
-
- cset "name='Headphone Left Switch' on"
- cset "name='Headphone Right Switch' on"
- cset "name='Headphone Switch' off"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
-
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- cset "name='Ext Spk Switch' on"
- ]
-
- DisableSequence [
- ]
-}
-
-SectionDevice."Headphone".0 {
- Value {
- JackName "byt-max98090 Headphone Jack"
- OutputDspName ""
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Speaker Left Switch' off"
- cset "name='Speaker Right Switch' off"
- cset "name='Headphone Switch' on"
- cset "name='HP Left Out Switch' on"
- cset "name='HP Right Out Switch' on"
- ]
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
- cset "name='Headphone Switch' off"
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- ]
-}
-
-SectionDevice."Mic".0 {
- Value {
- JackName "byt-max98090 Mic Jack"
- CaptureControl "MIC2"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Int Mic Switch' off"
- cset "name='Headset Mic Switch' on"
- cset "name='DMIC Mux' ADC"
- cset "name='Record Path DC Blocking' on"
- ]
-
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='Headset Mic Switch' off"
- cset "name='Int Mic Switch' on"
- cset "name='DMIC Mux' DMIC"
- cset "name='Record Path DC Blocking' off"
- ]
-}
diff --git a/ucm-config/squawks/byt-max98090/byt-max98090.conf b/ucm-config/squawks/byt-max98090/byt-max98090.conf
deleted file mode 100644
index 6a8972c1..00000000
--- a/ucm-config/squawks/byt-max98090/byt-max98090.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-Comment "Squawks internal card"
-
-SectionUseCase."HiFi" {
- File "HiFi.conf"
- Comment "Default"
-}
diff --git a/ucm-config/sumo/byt-max98090/HiFi.conf b/ucm-config/sumo/byt-max98090/HiFi.conf
deleted file mode 100644
index 75577c0a..00000000
--- a/ucm-config/sumo/byt-max98090/HiFi.conf
+++ /dev/null
@@ -1,97 +0,0 @@
-SectionVerb {
- Value {
- OutputDspName "speaker_eq"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Digital EQ 3 Band Switch' off"
- cset "name='Digital EQ 5 Band Switch' off"
- cset "name='Digital EQ 7 Band Switch' off"
- cset "name='Biquad Switch' off"
- cset "name='Filter Mode' Music"
- cset "name='ADC Oversampling Rate' 0"
-
- cset "name='DMIC Mux' DMIC"
- cset "name='MIC2 Mux' IN34"
- cset "name='Right ADC Mixer MIC2 Switch' on"
- cset "name='Left ADC Mixer MIC2 Switch' on"
- cset "name='MIC2 Volume' 20"
- cset "name='Int Mic Switch' on"
-
- cset "name='ADCR Boost Volume' 4"
- cset "name='ADCL Boost Volume' 4"
- cset "name='ADCR Volume' 11"
- cset "name='ADCL Volume' 11"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Speaker Left Mixer Volume' 2"
- cset "name='Speaker Right Mixer Volume' 2"
- cset "name='Record Path DC Blocking' on"
- cset "name='Playback Path DC Blocking' on"
-
- cset "name='Headphone Left Switch' on"
- cset "name='Headphone Right Switch' on"
- cset "name='Headphone Switch' off"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
-
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- cset "name='Ext Spk Switch' on"
- ]
-
- DisableSequence [
- ]
-}
-
-SectionDevice."Headphone".0 {
- Value {
- JackName "byt-max98090 Headphone Jack"
- OutputDspName ""
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Speaker Left Switch' off"
- cset "name='Speaker Right Switch' off"
- cset "name='Headphone Switch' on"
- cset "name='HP Left Out Switch' on"
- cset "name='HP Right Out Switch' on"
- ]
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
- cset "name='Headphone Switch' off"
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- ]
-}
-
-SectionDevice."Mic".0 {
- Value {
- JackName "byt-max98090 Mic Jack"
- CaptureControl "MIC2"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Int Mic Switch' off"
- cset "name='Headset Mic Switch' on"
- cset "name='DMIC Mux' ADC"
- cset "name='Record Path DC Blocking' on"
- ]
-
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='Headset Mic Switch' off"
- cset "name='Int Mic Switch' on"
- cset "name='DMIC Mux' DMIC"
- cset "name='Record Path DC Blocking' off"
- ]
-}
diff --git a/ucm-config/sumo/byt-max98090/byt-max98090.conf b/ucm-config/sumo/byt-max98090/byt-max98090.conf
deleted file mode 100644
index 47fee7ad..00000000
--- a/ucm-config/sumo/byt-max98090/byt-max98090.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-Comment "Sumo internal card"
-
-SectionUseCase."HiFi" {
- File "HiFi.conf"
- Comment "Default"
-}
diff --git a/ucm-config/veyron/ROCKCHIP-I2S/HiFi.conf b/ucm-config/veyron/ROCKCHIP-I2S/HiFi.conf
index 7bfe9959..f01190c3 100644
--- a/ucm-config/veyron/ROCKCHIP-I2S/HiFi.conf
+++ b/ucm-config/veyron/ROCKCHIP-I2S/HiFi.conf
@@ -2,6 +2,7 @@ SectionVerb {
Value {
OutputDspName "speaker_eq"
MinBufferLevel "512"
+ FullySpecifiedUCM "1"
}
EnableSequence [
@@ -47,8 +48,45 @@ SectionVerb {
]
}
+SectionDevice."Speaker".0 {
+ Value {
+ PlaybackPCM "hw:ROCKCHIPI2S,0"
+ MixerName "Speaker"
+ }
+ EnableSequence [
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Speaker Switch' on"
+ ]
+ DisableSequence [
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Speaker Switch' off"
+ ]
+}
+
+SectionDevice."Internal Mic".0 {
+ Value {
+ CapturePCM "hw:ROCKCHIPI2S,0"
+ MixerName "Int Mic"
+ }
+ EnableSequence [
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Int Mic Switch' on"
+ ]
+ DisableSequence [
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Int Mic Switch' off"
+ ]
+}
+
SectionDevice."Headphone".0 {
Value {
+ PlaybackPCM "hw:ROCKCHIPI2S,0"
+ MixerName "Headphone"
+ JackType "gpio"
JackName "ROCKCHIP-I2S Headset Jack"
OutputDspName ""
}
@@ -57,6 +95,7 @@ SectionDevice."Headphone".0 {
cdev "hw:ROCKCHIPI2S"
cset "name='Speaker Switch' off"
+ cset "name='Headphone Switch' on"
cset "name='Headphone Left Switch' on"
cset "name='Headphone Right Switch' on"
]
@@ -65,12 +104,16 @@ SectionDevice."Headphone".0 {
cset "name='Headphone Left Switch' off"
cset "name='Headphone Right Switch' off"
+ cset "name='Headphone Switch' off"
cset "name='Speaker Switch' on"
]
}
SectionDevice."Mic".0 {
Value {
+ CapturePCM "hw:ROCKCHIPI2S,0"
+ MixerName "Headset Mic"
+ JackType "gpio"
JackName "ROCKCHIP-I2S Headset Jack"
}
diff --git a/ucm-config/veyron/RockchipHDMI/HiFi.conf b/ucm-config/veyron/RockchipHDMI/HiFi.conf
index fc2eaaa2..890c97c6 100644
--- a/ucm-config/veyron/RockchipHDMI/HiFi.conf
+++ b/ucm-config/veyron/RockchipHDMI/HiFi.conf
@@ -1,6 +1,7 @@
SectionVerb {
Value {
OutputDspName ""
+ MinBufferLevel "512"
}
}
diff --git a/ucm-config/enguarde/byt-max98090/HiFi.conf b/ucm-config/veyron_fievel/ROCKCHIP-I2S/HiFi.conf
index 75577c0a..bf52f73e 100644
--- a/ucm-config/enguarde/byt-max98090/HiFi.conf
+++ b/ucm-config/veyron_fievel/ROCKCHIP-I2S/HiFi.conf
@@ -1,13 +1,17 @@
SectionVerb {
Value {
OutputDspName "speaker_eq"
+ MinBufferLevel "512"
+ FullySpecifiedUCM "1"
}
EnableSequence [
- cdev "hw:bytmax98090"
+ cdev "hw:ROCKCHIPI2S"
cset "name='Left Speaker Mixer Left DAC Switch' on"
cset "name='Right Speaker Mixer Right DAC Switch' on"
+ cset "name='Headphone Left Switch' off"
+ cset "name='Headphone Right Switch' off"
cset "name='Digital EQ 3 Band Switch' off"
cset "name='Digital EQ 5 Band Switch' off"
cset "name='Digital EQ 7 Band Switch' off"
@@ -20,6 +24,7 @@ SectionVerb {
cset "name='Right ADC Mixer MIC2 Switch' on"
cset "name='Left ADC Mixer MIC2 Switch' on"
cset "name='MIC2 Volume' 20"
+ cset "name='Headset Mic Switch' off"
cset "name='Int Mic Switch' on"
cset "name='ADCR Boost Volume' 4"
@@ -34,15 +39,9 @@ SectionVerb {
cset "name='Record Path DC Blocking' on"
cset "name='Playback Path DC Blocking' on"
- cset "name='Headphone Left Switch' on"
- cset "name='Headphone Right Switch' on"
- cset "name='Headphone Switch' off"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
-
cset "name='Speaker Left Switch' on"
cset "name='Speaker Right Switch' on"
- cset "name='Ext Spk Switch' on"
+ cset "name='Speaker Switch' on"
]
DisableSequence [
@@ -51,47 +50,54 @@ SectionVerb {
SectionDevice."Headphone".0 {
Value {
- JackName "byt-max98090 Headphone Jack"
+ PlaybackPCM "hw:ROCKCHIPI2S,0"
+ MixerName "Headphone"
+ JackType "gpio"
+ JackName "ROCKCHIP-I2S Headset Jack"
OutputDspName ""
}
EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Speaker Left Switch' off"
- cset "name='Speaker Right Switch' off"
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Speaker Switch' off"
cset "name='Headphone Switch' on"
- cset "name='HP Left Out Switch' on"
- cset "name='HP Right Out Switch' on"
+ cset "name='Headphone Left Switch' on"
+ cset "name='Headphone Right Switch' on"
]
DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Headphone Left Switch' off"
+ cset "name='Headphone Right Switch' off"
cset "name='Headphone Switch' off"
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
+ cset "name='Speaker Switch' on"
]
}
SectionDevice."Mic".0 {
Value {
- JackName "byt-max98090 Mic Jack"
- CaptureControl "MIC2"
+ CapturePCM "hw:ROCKCHIPI2S,0"
+ MixerName "Headset Mic"
+ JackType "gpio"
+ JackName "ROCKCHIP-I2S Headset Jack"
}
EnableSequence [
- cdev "hw:bytmax98090"
+ cdev "hw:ROCKCHIPI2S"
+
cset "name='Int Mic Switch' off"
- cset "name='Headset Mic Switch' on"
cset "name='DMIC Mux' ADC"
+ cset "name='Headset Mic Switch' on"
cset "name='Record Path DC Blocking' on"
]
DisableSequence [
- cdev "hw:bytmax98090"
+ cdev "hw:ROCKCHIPI2S"
+
cset "name='Headset Mic Switch' off"
- cset "name='Int Mic Switch' on"
cset "name='DMIC Mux' DMIC"
+ cset "name='Int Mic Switch' on"
cset "name='Record Path DC Blocking' off"
]
}
diff --git a/ucm-config/kip/byt-max98090/byt-max98090.conf b/ucm-config/veyron_fievel/ROCKCHIP-I2S/ROCKCHIP-I2S.conf
index 0d519349..0c399b0d 100644
--- a/ucm-config/kip/byt-max98090/byt-max98090.conf
+++ b/ucm-config/veyron_fievel/ROCKCHIP-I2S/ROCKCHIP-I2S.conf
@@ -1,4 +1,4 @@
-Comment "Kip internal card"
+Comment "Rockchip card"
SectionUseCase."HiFi" {
File "HiFi.conf"
diff --git a/ucm-config/veyron_fievel/RockchipHDMI/HiFi.conf b/ucm-config/veyron_fievel/RockchipHDMI/HiFi.conf
new file mode 100644
index 00000000..890c97c6
--- /dev/null
+++ b/ucm-config/veyron_fievel/RockchipHDMI/HiFi.conf
@@ -0,0 +1,14 @@
+SectionVerb {
+ Value {
+ OutputDspName ""
+ MinBufferLevel "512"
+ }
+}
+
+SectionDevice."HDMI".0 {
+ Value {
+ JackName "RockchipHDMI HDMI Jack"
+ OutputDspName ""
+ EDIDFile "/sys/class/drm/card1-HDMI-A-1/edid"
+ }
+}
diff --git a/ucm-config/orco/byt-max98090/byt-max98090.conf b/ucm-config/veyron_fievel/RockchipHDMI/RockchipHDMI.conf
index 6d3deecc..a167d741 100644
--- a/ucm-config/orco/byt-max98090/byt-max98090.conf
+++ b/ucm-config/veyron_fievel/RockchipHDMI/RockchipHDMI.conf
@@ -1,4 +1,4 @@
-Comment "Orco internal card"
+Comment "Rockchip HDMI card"
SectionUseCase."HiFi" {
File "HiFi.conf"
diff --git a/ucm-config/veyron_jaq/ROCKCHIP-I2S/HiFi.conf b/ucm-config/veyron_jaq/ROCKCHIP-I2S/HiFi.conf
index f30fa3d2..47012622 100644
--- a/ucm-config/veyron_jaq/ROCKCHIP-I2S/HiFi.conf
+++ b/ucm-config/veyron_jaq/ROCKCHIP-I2S/HiFi.conf
@@ -2,6 +2,7 @@ SectionVerb {
Value {
OutputDspName "speaker_eq"
MinBufferLevel "512"
+ FullySpecifiedUCM "1"
}
EnableSequence [
@@ -52,8 +53,45 @@ SectionVerb {
]
}
+SectionDevice."Speaker".0 {
+ Value {
+ PlaybackPCM "hw:ROCKCHIPI2S,0"
+ MixerName "Speaker"
+ }
+ EnableSequence [
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Speaker Switch' on"
+ ]
+ DisableSequence [
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Speaker Switch' off"
+ ]
+}
+
+SectionDevice."Internal Mic".0 {
+ Value {
+ CapturePCM "hw:ROCKCHIPI2S,0"
+ MixerName "Int Mic"
+ }
+ EnableSequence [
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Int Mic Switch' on"
+ ]
+ DisableSequence [
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Int Mic Switch' off"
+ ]
+}
+
SectionDevice."Headphone".0 {
Value {
+ PlaybackPCM "hw:ROCKCHIPI2S,0"
+ MixerName "Headphone"
+ JackType "gpio"
JackName "ROCKCHIP-I2S Headset Jack"
OutputDspName ""
}
@@ -62,6 +100,7 @@ SectionDevice."Headphone".0 {
cdev "hw:ROCKCHIPI2S"
cset "name='Speaker Switch' off"
+ cset "name='Headphone Switch' on"
cset "name='Headphone Left Switch' on"
cset "name='Headphone Right Switch' on"
]
@@ -70,12 +109,16 @@ SectionDevice."Headphone".0 {
cset "name='Headphone Left Switch' off"
cset "name='Headphone Right Switch' off"
+ cset "name='Headphone Switch' off"
cset "name='Speaker Switch' on"
]
}
SectionDevice."Mic".0 {
Value {
+ CapturePCM "hw:ROCKCHIPI2S,0"
+ MixerName "Headset Mic"
+ JackType "gpio"
JackName "ROCKCHIP-I2S Headset Jack"
}
diff --git a/ucm-config/veyron_jaq/RockchipHDMI/HiFi.conf b/ucm-config/veyron_jaq/RockchipHDMI/HiFi.conf
index fc2eaaa2..890c97c6 100644
--- a/ucm-config/veyron_jaq/RockchipHDMI/HiFi.conf
+++ b/ucm-config/veyron_jaq/RockchipHDMI/HiFi.conf
@@ -1,6 +1,7 @@
SectionVerb {
Value {
OutputDspName ""
+ MinBufferLevel "512"
}
}
diff --git a/ucm-config/veyron_minnie/ROCKCHIP-I2S/HiFi.conf b/ucm-config/veyron_minnie/ROCKCHIP-I2S/HiFi.conf
index 4032e604..ce29a3f5 100644
--- a/ucm-config/veyron_minnie/ROCKCHIP-I2S/HiFi.conf
+++ b/ucm-config/veyron_minnie/ROCKCHIP-I2S/HiFi.conf
@@ -2,6 +2,7 @@ SectionVerb {
Value {
OutputDspName "speaker_eq"
MinBufferLevel "512"
+ FullySpecifiedUCM "1"
}
EnableSequence [
@@ -47,8 +48,45 @@ SectionVerb {
]
}
+SectionDevice."Speaker".0 {
+ Value {
+ PlaybackPCM "hw:ROCKCHIPI2S,0"
+ MixerName "Speaker"
+ }
+ EnableSequence [
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Speaker Switch' on"
+ ]
+ DisableSequence [
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Speaker Switch' off"
+ ]
+}
+
+SectionDevice."Internal Mic".0 {
+ Value {
+ CapturePCM "hw:ROCKCHIPI2S,0"
+ MixerName "Int Mic"
+ }
+ EnableSequence [
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Int Mic Switch' on"
+ ]
+ DisableSequence [
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Int Mic Switch' off"
+ ]
+}
+
SectionDevice."Headphone".0 {
Value {
+ PlaybackPCM "hw:ROCKCHIPI2S,0"
+ MixerName "Headphone"
+ JackType "gpio"
JackName "ROCKCHIP-I2S Headset Jack"
OutputDspName ""
}
@@ -57,6 +95,7 @@ SectionDevice."Headphone".0 {
cdev "hw:ROCKCHIPI2S"
cset "name='Speaker Switch' off"
+ cset "name='Headphone Switch' on"
cset "name='Headphone Left Switch' on"
cset "name='Headphone Right Switch' on"
]
@@ -65,12 +104,16 @@ SectionDevice."Headphone".0 {
cset "name='Headphone Left Switch' off"
cset "name='Headphone Right Switch' off"
+ cset "name='Headphone Switch' off"
cset "name='Speaker Switch' on"
]
}
SectionDevice."Mic".0 {
Value {
+ CapturePCM "hw:ROCKCHIPI2S,0"
+ MixerName "Headset Mic"
+ JackType "gpio"
JackName "ROCKCHIP-I2S Headset Jack"
}
@@ -92,25 +135,3 @@ SectionDevice."Mic".0 {
cset "name='Record Path DC Blocking' off"
]
}
-
-SectionModifier."Speaker Swap Mode".0 {
- Comment "Swap the left and right channels of speaker."
-
- EnableSequence [
- cdev "hw:ROCKCHIPI2S"
-
- cset "name='Left Speaker Mixer Left DAC Switch' off"
- cset "name='Right Speaker Mixer Right DAC Switch' off"
- cset "name='Left Speaker Mixer Right DAC Switch' on"
- cset "name='Right Speaker Mixer Left DAC Switch' on"
- ]
-
- DisableSequence [
- cdev "hw:ROCKCHIPI2S"
-
- cset "name='Left Speaker Mixer Right DAC Switch' off"
- cset "name='Right Speaker Mixer Left DAC Switch' off"
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- ]
-}
diff --git a/ucm-config/veyron_minnie/RockchipHDMI/HiFi.conf b/ucm-config/veyron_minnie/RockchipHDMI/HiFi.conf
index fc2eaaa2..890c97c6 100644
--- a/ucm-config/veyron_minnie/RockchipHDMI/HiFi.conf
+++ b/ucm-config/veyron_minnie/RockchipHDMI/HiFi.conf
@@ -1,6 +1,7 @@
SectionVerb {
Value {
OutputDspName ""
+ MinBufferLevel "512"
}
}
diff --git a/ucm-config/veyron_speedy/ROCKCHIP-I2S/HiFi.conf b/ucm-config/veyron_speedy/ROCKCHIP-I2S/HiFi.conf
index 0177febc..ce29a3f5 100644
--- a/ucm-config/veyron_speedy/ROCKCHIP-I2S/HiFi.conf
+++ b/ucm-config/veyron_speedy/ROCKCHIP-I2S/HiFi.conf
@@ -2,6 +2,7 @@ SectionVerb {
Value {
OutputDspName "speaker_eq"
MinBufferLevel "512"
+ FullySpecifiedUCM "1"
}
EnableSequence [
@@ -47,8 +48,45 @@ SectionVerb {
]
}
+SectionDevice."Speaker".0 {
+ Value {
+ PlaybackPCM "hw:ROCKCHIPI2S,0"
+ MixerName "Speaker"
+ }
+ EnableSequence [
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Speaker Switch' on"
+ ]
+ DisableSequence [
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Speaker Switch' off"
+ ]
+}
+
+SectionDevice."Internal Mic".0 {
+ Value {
+ CapturePCM "hw:ROCKCHIPI2S,0"
+ MixerName "Int Mic"
+ }
+ EnableSequence [
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Int Mic Switch' on"
+ ]
+ DisableSequence [
+ cdev "hw:ROCKCHIPI2S"
+
+ cset "name='Int Mic Switch' off"
+ ]
+}
+
SectionDevice."Headphone".0 {
Value {
+ PlaybackPCM "hw:ROCKCHIPI2S,0"
+ MixerName "Headphone"
+ JackType "gpio"
JackName "ROCKCHIP-I2S Headset Jack"
OutputDspName ""
}
@@ -57,6 +95,7 @@ SectionDevice."Headphone".0 {
cdev "hw:ROCKCHIPI2S"
cset "name='Speaker Switch' off"
+ cset "name='Headphone Switch' on"
cset "name='Headphone Left Switch' on"
cset "name='Headphone Right Switch' on"
]
@@ -65,12 +104,16 @@ SectionDevice."Headphone".0 {
cset "name='Headphone Left Switch' off"
cset "name='Headphone Right Switch' off"
+ cset "name='Headphone Switch' off"
cset "name='Speaker Switch' on"
]
}
SectionDevice."Mic".0 {
Value {
+ CapturePCM "hw:ROCKCHIPI2S,0"
+ MixerName "Headset Mic"
+ JackType "gpio"
JackName "ROCKCHIP-I2S Headset Jack"
}
diff --git a/ucm-config/veyron_speedy/RockchipHDMI/HiFi.conf b/ucm-config/veyron_speedy/RockchipHDMI/HiFi.conf
index fc2eaaa2..890c97c6 100644
--- a/ucm-config/veyron_speedy/RockchipHDMI/HiFi.conf
+++ b/ucm-config/veyron_speedy/RockchipHDMI/HiFi.conf
@@ -1,6 +1,7 @@
SectionVerb {
Value {
OutputDspName ""
+ MinBufferLevel "512"
}
}
diff --git a/ucm-config/winky/byt-max98090/HiFi.conf b/ucm-config/winky/byt-max98090/HiFi.conf
deleted file mode 100644
index 75577c0a..00000000
--- a/ucm-config/winky/byt-max98090/HiFi.conf
+++ /dev/null
@@ -1,97 +0,0 @@
-SectionVerb {
- Value {
- OutputDspName "speaker_eq"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Digital EQ 3 Band Switch' off"
- cset "name='Digital EQ 5 Band Switch' off"
- cset "name='Digital EQ 7 Band Switch' off"
- cset "name='Biquad Switch' off"
- cset "name='Filter Mode' Music"
- cset "name='ADC Oversampling Rate' 0"
-
- cset "name='DMIC Mux' DMIC"
- cset "name='MIC2 Mux' IN34"
- cset "name='Right ADC Mixer MIC2 Switch' on"
- cset "name='Left ADC Mixer MIC2 Switch' on"
- cset "name='MIC2 Volume' 20"
- cset "name='Int Mic Switch' on"
-
- cset "name='ADCR Boost Volume' 4"
- cset "name='ADCL Boost Volume' 4"
- cset "name='ADCR Volume' 11"
- cset "name='ADCL Volume' 11"
-
- cset "name='Left Speaker Mixer Left DAC Switch' on"
- cset "name='Right Speaker Mixer Right DAC Switch' on"
- cset "name='Speaker Left Mixer Volume' 2"
- cset "name='Speaker Right Mixer Volume' 2"
- cset "name='Record Path DC Blocking' on"
- cset "name='Playback Path DC Blocking' on"
-
- cset "name='Headphone Left Switch' on"
- cset "name='Headphone Right Switch' on"
- cset "name='Headphone Switch' off"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
-
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- cset "name='Ext Spk Switch' on"
- ]
-
- DisableSequence [
- ]
-}
-
-SectionDevice."Headphone".0 {
- Value {
- JackName "byt-max98090 Headphone Jack"
- OutputDspName ""
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Speaker Left Switch' off"
- cset "name='Speaker Right Switch' off"
- cset "name='Headphone Switch' on"
- cset "name='HP Left Out Switch' on"
- cset "name='HP Right Out Switch' on"
- ]
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='HP Left Out Switch' off"
- cset "name='HP Right Out Switch' off"
- cset "name='Headphone Switch' off"
- cset "name='Speaker Left Switch' on"
- cset "name='Speaker Right Switch' on"
- ]
-}
-
-SectionDevice."Mic".0 {
- Value {
- JackName "byt-max98090 Mic Jack"
- CaptureControl "MIC2"
- }
-
- EnableSequence [
- cdev "hw:bytmax98090"
- cset "name='Int Mic Switch' off"
- cset "name='Headset Mic Switch' on"
- cset "name='DMIC Mux' ADC"
- cset "name='Record Path DC Blocking' on"
- ]
-
- DisableSequence [
- cdev "hw:bytmax98090"
- cset "name='Headset Mic Switch' off"
- cset "name='Int Mic Switch' on"
- cset "name='DMIC Mux' DMIC"
- cset "name='Record Path DC Blocking' off"
- ]
-}
diff --git a/ucm-config/winky/byt-max98090/byt-max98090.conf b/ucm-config/winky/byt-max98090/byt-max98090.conf
deleted file mode 100644
index 915d1551..00000000
--- a/ucm-config/winky/byt-max98090/byt-max98090.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-Comment "Rambi internal card"
-
-SectionUseCase."HiFi" {
- File "HiFi.conf"
- Comment "Default"
-}
diff --git a/upstart/cras.conf b/upstart/cras.conf
deleted file mode 100644
index 28c13e22..00000000
--- a/upstart/cras.conf
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright (c) 2012 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.
-
-# Installed by ADHD package.
-# cras upstart job.
-
-description "ChromeOS audio server"
-author "chromium-os-dev@chromium.org"
-
-env CRAS_SOCKET_DIR=/var/run/cras
-
-start on starting system-services
-stop on stopping system-services
-respawn
-
-# Allow the audio server real time priority.
-limit rtprio 12 12
-
-pre-start script
- mkdir -p -m 1770 "${CRAS_SOCKET_DIR}"
- chown -R cras:cras "${CRAS_SOCKET_DIR}"
-end script
-
-script
- # Use /etc/cras/enable_hfp as a flag to enable HFP/HSP
- # support for testing purpose. In test image, touch or
- # rm this file to toggle this feature.
- # TODO(hychao): remove this temporary flag when we pass
- # qualification and ready to launch this feature.
- if [ -f /etc/cras/enable_hfp ]; then
- ENABLE_HFP="--enable_hfp"
- else
- ENABLE_HFP=""
- fi
-
- # For Samus only, check which dsp.ini to load.
- if [ "$(mosys platform name)" = "Samus" ]; then
- local hw_version="$(mosys platform version)"
- if [ "$hw_version" = "MP.A" ] ||
- [ "$hw_version" = "EVT" ] ||
- [ "$hw_version" = "DVT" ] ||
- [ "$hw_version" = "PVT" ]; then
- DSP_CONFIG="--dsp_config=/etc/cras/dsp.samus.orig.ini"
- fi
- fi
- # For board needs different device configs, check which config
- # directory to use. Use that directory for both volume curves
- # and dsp config.
- if [ -f /etc/cras/get_device_config_dir ]; then
- local device_config_dir="$(sh /etc/cras/get_device_config_dir)"
- DEVICE_CONFIG_DIR="--device_config_dir=${device_config_dir}"
- DSP_CONFIG="--dsp_config=${device_config_dir}/dsp.ini"
- fi
- exec minijail0 -u cras -g cras -G -- /usr/bin/cras ${ENABLE_HFP} \
- ${DSP_CONFIG} ${DEVICE_CONFIG_DIR}
-end script