1 # Copyright 2016 The Chromium OS Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 import logging 6 import os 7 import re 8 9 from autotest_lib.client.bin import test, utils 10 from autotest_lib.client.common_lib import error 11 from autotest_lib.client.cros.audio import alsa_utils 12 13 class audio_AlsaAPI(test.test): 14 """Checks that simple ALSA API functions correctly.""" 15 version = 2 16 _SND_DEV_DIR = '/dev/snd/' 17 _PLAYBACK_DEVICE_NAME = '^pcmC(\d+)D(\d+)p$' 18 # A list of boards that do not correctly implement snd_pcm_drop, see 19 # crosbug.com/p/51882 20 _BOARDS_WITHOUT_DROP_SUPPORT = ['banon', 'elm', 'samus', 'squawks'] 21 # A dict of list of (card name, device) to be skipped on some boards. 22 _DEVICES_TO_BE_SKIPPED = { 23 # On the following boards, devices 4,5,6 are HDMI devices. 24 'asuka': {'sklnau8825max': [4, 5, 6]}, 25 'cave': {'sklnau8825max': [4, 5, 6]}, 26 'snappy': {'bxtda7219max': [4, 5, 6]}, 27 # Chell's HDMI device 4 can not be used without being plugged. 28 # Also, its HDMI devices 5 and 6 are dummy devices. 29 'chell': {'sklnau8825adi': [4, 5, 6]}, 30 # Kevin's device 3 is a DisplayPort device. 31 'kevin': {'rk3399-gru-sound': [3]}, 32 } 33 34 def run_once(self, to_test): 35 """Run alsa_api_test binary and verify its result. 36 37 Checks the source code of alsa_api_test in audiotest repo for detail. 38 39 @param to_test: support these test items: 40 move: Checks snd_pcm_forward API. 41 fill: Checks snd_pcm_mmap_begin API. 42 drop: Checks snd_pcm_drop API. 43 44 """ 45 # Skip test_drop on boards that do not implement snd_pcm_drop 46 # correctly, as it cannot pass. 47 board = utils.get_board().lower() 48 if to_test == 'drop' and board in self._BOARDS_WITHOUT_DROP_SUPPORT: 49 logging.info('Skipping test_drop for unsupported board: %s', board) 50 return 51 52 self._cardnames = alsa_utils.get_soundcard_names() 53 self._devices = [] 54 self._find_sound_devices() 55 method_name = '_test_' + to_test 56 method = getattr(self, method_name) 57 58 # Stop CRAS to make sure the audio device won't be occupied. 59 utils.stop_service('cras', ignore_status=True) 60 61 try: 62 for card_index, device_index in self._devices: 63 device = 'hw:%s,%s' % (card_index, device_index) 64 method(device) 65 finally: 66 # Restart CRAS. 67 utils.start_service('cras', ignore_status=True) 68 69 70 def _skip_device(self, card_device): 71 """Skips devices on some boards. 72 73 @param card_device: A tuple of (card index, device index). 74 75 @returns: True if the device should be skipped. False otherwise. 76 77 """ 78 card_name = self._cardnames[card_device[0]] 79 80 return card_device[1] in self._DEVICES_TO_BE_SKIPPED.get( 81 utils.get_board().lower(), {}).get(card_name, []) 82 83 84 def _find_sound_devices(self): 85 """Finds playback devices in sound device directory. 86 87 @raises: error.TestError if there is no playback device. 88 """ 89 filenames = os.listdir(self._SND_DEV_DIR) 90 for filename in filenames: 91 search = re.match(self._PLAYBACK_DEVICE_NAME, filename) 92 if search: 93 card_device = (search.group(1), int(search.group(2))) 94 if not self._skip_device(card_device): 95 self._devices.append(card_device) 96 if not self._devices: 97 raise error.TestError('There is no playback device') 98 99 100 def _make_alsa_api_test_command(self, option, device): 101 """Makes command for alsa_api_test. 102 103 @param option: same as to_test in run_once. 104 @param device: device in hw:<card index>:<device index> format. 105 106 @returns: The command in a list of args. 107 108 """ 109 return ['alsa_api_test', '--device', device, '--%s' % option] 110 111 112 def _test_move(self, device): 113 """Runs alsa_api_test command and checks the return code. 114 115 Test snd_pcm_forward can move appl_ptr to hw_ptr. 116 117 @param device: device in hw:<card index>:<device index> format. 118 119 @raises error.TestError if command fails. 120 121 """ 122 ret = utils.system( 123 command=self._make_alsa_api_test_command('move', device), 124 ignore_status=True) 125 if ret: 126 raise error.TestError( 127 'ALSA API failed to move appl_ptr on device %s' % device) 128 129 130 def _test_fill(self, device): 131 """Runs alsa_api_test command and checks the return code. 132 133 Test snd_pcm_mmap_begin can provide the access to the buffer, and memset 134 can fill it with zeros without using snd_pcm_mmap_commit. 135 136 @param device: device in hw:<card index>:<device index> format. 137 138 @raises error.TestError if command fails. 139 140 """ 141 ret = utils.system( 142 command=self._make_alsa_api_test_command('fill', device), 143 ignore_status=True) 144 if ret: 145 raise error.TestError( 146 'ALSA API failed to fill buffer on device %s' % device) 147 148 149 def _test_drop(self, device): 150 """Runs alsa_api_test command and checks the return code. 151 152 Test snd_pcm_drop can stop playback and reset hw_ptr to 0 in hardware. 153 154 @param device: device in hw:<card index>:<device index> format. 155 156 @raises error.TestError if command fails. 157 158 """ 159 ret = utils.system( 160 command=self._make_alsa_api_test_command('drop', device), 161 ignore_status=True) 162 if ret: 163 raise error.TestError( 164 'ALSA API failed to drop playback and reset hw_ptr' 165 'on device %s' % device) 166