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 5import logging 6import os 7import re 8 9from autotest_lib.client.bin import test, utils 10from autotest_lib.client.common_lib import error 11from autotest_lib.client.cros.audio import alsa_utils 12 13class 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 \ 49 board.replace('-kernelnext', '') in \ 50 self._BOARDS_WITHOUT_DROP_SUPPORT: 51 logging.info('Skipping test_drop for unsupported board: %s', board) 52 return 53 54 self._cardnames = alsa_utils.get_soundcard_names() 55 self._devices = [] 56 self._find_sound_devices() 57 method_name = '_test_' + to_test 58 method = getattr(self, method_name) 59 60 # Stop CRAS to make sure the audio device won't be occupied. 61 utils.stop_service('cras', ignore_status=True) 62 63 try: 64 for card_index, device_index in self._devices: 65 device = 'hw:%s,%s' % (card_index, device_index) 66 method(device) 67 finally: 68 # Restart CRAS. 69 utils.start_service('cras', ignore_status=True) 70 71 72 def _skip_device(self, card_device): 73 """Skips devices on some boards. 74 75 @param card_device: A tuple of (card index, device index). 76 77 @returns: True if the device should be skipped. False otherwise. 78 79 """ 80 card_name = self._cardnames[card_device[0]] 81 82 return card_device[1] in self._DEVICES_TO_BE_SKIPPED.get( 83 utils.get_board().lower(), {}).get(card_name, []) 84 85 86 def _find_sound_devices(self): 87 """Finds playback devices in sound device directory. 88 89 @raises: error.TestError if there is no playback device. 90 """ 91 filenames = os.listdir(self._SND_DEV_DIR) 92 for filename in filenames: 93 search = re.match(self._PLAYBACK_DEVICE_NAME, filename) 94 if search: 95 card_device = (search.group(1), int(search.group(2))) 96 if not self._skip_device(card_device): 97 self._devices.append(card_device) 98 if not self._devices: 99 raise error.TestError('There is no playback device') 100 101 102 def _make_alsa_api_test_command(self, option, device): 103 """Makes command for alsa_api_test. 104 105 @param option: same as to_test in run_once. 106 @param device: device in hw:<card index>:<device index> format. 107 108 @returns: The command in a list of args. 109 110 """ 111 return ['alsa_api_test', '--device', device, '--%s' % option] 112 113 114 def _test_move(self, device): 115 """Runs alsa_api_test command and checks the return code. 116 117 Test snd_pcm_forward can move appl_ptr to hw_ptr. 118 119 @param device: device in hw:<card index>:<device index> format. 120 121 @raises error.TestError if command fails. 122 123 """ 124 ret = utils.system( 125 command=self._make_alsa_api_test_command('move', device), 126 ignore_status=True) 127 if ret: 128 raise error.TestError( 129 'ALSA API failed to move appl_ptr on device %s' % device) 130 131 132 def _test_fill(self, device): 133 """Runs alsa_api_test command and checks the return code. 134 135 Test snd_pcm_mmap_begin can provide the access to the buffer, and memset 136 can fill it with zeros without using snd_pcm_mmap_commit. 137 138 @param device: device in hw:<card index>:<device index> format. 139 140 @raises error.TestError if command fails. 141 142 """ 143 ret = utils.system( 144 command=self._make_alsa_api_test_command('fill', device), 145 ignore_status=True) 146 if ret: 147 raise error.TestError( 148 'ALSA API failed to fill buffer on device %s' % device) 149 150 151 def _test_drop(self, device): 152 """Runs alsa_api_test command and checks the return code. 153 154 Test snd_pcm_drop can stop playback and reset hw_ptr to 0 in hardware. 155 156 @param device: device in hw:<card index>:<device index> format. 157 158 @raises error.TestError if command fails. 159 160 """ 161 ret = utils.system( 162 command=self._make_alsa_api_test_command('drop', device), 163 ignore_status=True) 164 if ret: 165 raise error.TestError( 166 'ALSA API failed to drop playback and reset hw_ptr' 167 'on device %s' % device) 168