diff options
author | Dylan Reid <dgreid@chromium.org> | 2017-06-07 09:50:59 -0700 |
---|---|---|
committer | Dylan Reid <dgreid@google.com> | 2017-06-07 09:53:49 -0700 |
commit | d4592bd501f5898d7f00e709711d896e04a88c40 (patch) | |
tree | 022092b89799875b28fcd9457b63521a75b914e3 | |
parent | 501f4deaee648493ff2b6e7e22df1f1f8d4c1517 (diff) | |
parent | 81b3032369be1bdb5976a1f7772b6c9fb94229ac (diff) | |
download | adhd-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>
274 files changed, 35351 insertions, 10492 deletions
@@ -1,2 +1,3 @@ build cscope.* +PRESUBMIT.cfg @@ -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) @@ -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(®ex, re, REG_EXTENDED); + if (rc != 0) { + syslog(LOG_ERR, "Failed to compile regular expression: %s", re); + return 0; + } + + rc = regexec(®ex, jack_name, ARRAY_SIZE(m), m, 0) == 0; + regfree(®ex); + 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(®ex, re); - success = regexec(®ex, jack, ARRAY_SIZE(m), m, 0) == 0; - regfree(®ex); - 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(©_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 |