1# Lint as: python2, python3 2# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6import os 7import logging 8import time 9 10from autotest_lib.client.bin import test, utils 11from autotest_lib.client.common_lib import error 12from autotest_lib.client.cros import upstart 13from autotest_lib.client.cros.audio import alsa_utils 14from autotest_lib.client.cros.audio import audio_helper 15from autotest_lib.client.cros.audio import audio_spec 16from autotest_lib.client.cros.audio import cras_utils 17 18APLAY_FILE = '/dev/zero' # raw data 19 20# Expected results of 'aplay -v' commands. 21APLAY_EXPECTED = set([('stream', 'PLAYBACK')]) 22 23 24def _play_audio(device_name, duration=1, channel_count=2): 25 """Play a tone and try to ensure it played properly. 26 27 Sample output from aplay -v: 28 29 Playing raw data '/dev/zero' : Signed 16 bit Little Endian, Rate 44100 Hz, 30 Stereo 31 Hardware PCM card 0 'HDA Intel PCH' device 0 subdevice 0 32 Its setup is: 33 stream : PLAYBACK 34 access : RW_INTERLEAVED format : S16_LE 35 subformat : STD 36 channels : 2 37 rate : 44100 38 exact rate : 44100 (44100/1) 39 msbits : 16 40 buffer_size : 16384 41 period_size : 4096 42 period_time : 92879 43 tstamp_mode : NONE 44 period_step : 1 45 avail_min : 4096 46 period_event : 0 47 start_threshold : 16384 48 stop_threshold : 16384 49 silence_threshold: 0 50 silence_size : 0 51 boundary : 4611686018427387904 52 appl_ptr : 0 53 hw_ptr : 0 54 55 @param device_name: The output device for aplay. 56 @param duration: Duration supplied to aplay. 57 @return String output from the command (may be empty). 58 @raises CmdError when cmd returns <> 0. 59 """ 60 cmd = [ 61 'aplay', 62 '-v', # show verbose details 63 '-D %s' % device_name, 64 '-d %d' % duration, 65 '-c %d' % channel_count, 66 '-r 44100', 67 '-f S16_LE', 68 APLAY_FILE, 69 '2>&1' 70 ] # verbose details 71 return utils.system_output(' '.join(cmd)).strip() 72 73 74def _check_play(device_name, duration, channel_count, expected): 75 """Runs aplay command and checks the output against an expected result. 76 77 The expected results are compared as sets of tuples. 78 79 @param device_name: The output device for aplay. 80 @param duration: Duration supplied to aplay. 81 @param channel_count: Channel count supplied to aplay 82 @param expected: The set of expected tuples. 83 @raises error.TestError for invalid output or invalidly matching expected. 84 """ 85 error_msg = 'invalid response from aplay' 86 results = _play_audio(device_name, duration, channel_count) 87 if not results.startswith("Playing raw data '%s' :" % APLAY_FILE): 88 raise error.TestError('%s: %s' % (error_msg, results)) 89 result_set = utils.set_from_keyval_output(results, '[\s]*:[\s]*') 90 if set(expected) <= result_set: 91 return 92 raise error.TestError('%s: expected=%s.' % 93 (error_msg, sorted(set(expected) - result_set))) 94 95 96class audio_Aplay(test.test): 97 """Checks that simple aplay functions correctly.""" 98 version = 1 99 100 def initialize(self): 101 """Stop ui while running the test.""" 102 upstart.stop_job('ui') 103 104 def cleanup(self): 105 """Start ui back after the test.""" 106 upstart.restart_job('ui') 107 108 def run_once(self, duration=1, test_headphone=False): 109 """Run aplay and verify its output is as expected. 110 111 @param duration: The duration to run aplay in seconds. 112 @param test_headphone: If the value is true, test a headphone. If false, 113 test an internal speaker. 114 """ 115 116 # Check CRAS server is alive. If not, restart it and wait a second to 117 # get server ready. 118 if utils.get_service_pid('cras') == 0: 119 logging.debug("CRAS server is down. Restart it.") 120 utils.start_service('cras', ignore_status=True) 121 time.sleep(1) 122 123 # Skip test if there is no internal speaker on the board. 124 if not test_headphone: 125 board_type = utils.get_board_type() 126 board_name = utils.get_board() 127 if not audio_spec.has_internal_speaker(board_type, board_name): 128 logging.debug("No internal speaker. Skipping the test.") 129 return 130 131 if test_headphone: 132 output_node = audio_spec.get_headphone_node(utils.get_board()) 133 channel_count = 2 134 else: 135 output_node = "INTERNAL_SPEAKER" 136 channel_count = audio_spec.get_internal_speaker_channel_count( 137 utils.get_board_type(), utils.get_board(), 138 utils.get_platform(), utils.get_sku()) 139 logging.debug("Test output device %s", output_node) 140 141 cras_utils.set_single_selected_output_node(output_node) 142 143 cras_device_type = cras_utils.get_selected_output_device_type() 144 logging.debug("Selected output device type=%s", cras_device_type) 145 if cras_device_type != output_node: 146 audio_helper.dump_audio_diagnostics( 147 os.path.join(self.resultsdir, "audio_diagnostics.txt")) 148 raise error.TestFail("Fail to select output device.") 149 150 cras_device_name = cras_utils.get_selected_output_device_name() 151 logging.debug("Selected output device name=%s", cras_device_name) 152 if cras_device_name is None: 153 audio_helper.dump_audio_diagnostics( 154 os.path.join(self.resultsdir, "audio_diagnostics.txt")) 155 raise error.TestFail("Fail to get selected output device.") 156 157 alsa_device_name = alsa_utils.convert_device_name(cras_device_name) 158 159 # Stop CRAS to make sure the audio device won't be occupied. 160 utils.stop_service('cras', ignore_status=True) 161 try: 162 _check_play(alsa_device_name, duration, channel_count, 163 APLAY_EXPECTED) 164 finally: 165 #Restart CRAS 166 utils.start_service('cras', ignore_status=True) 167