| # 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. |
| |
| import logging |
| import os |
| import re |
| |
| from autotest_lib.client.bin import test, utils |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.client.cros.audio import alsa_utils |
| |
| class audio_AlsaAPI(test.test): |
| """Checks that simple ALSA API functions correctly.""" |
| version = 2 |
| _SND_DEV_DIR = '/dev/snd/' |
| _PLAYBACK_DEVICE_NAME = '^pcmC(\d+)D(\d+)p$' |
| # A list of boards that do not correctly implement snd_pcm_drop, see |
| # crosbug.com/p/51882 |
| _BOARDS_WITHOUT_DROP_SUPPORT = ['banon', 'elm', 'samus', 'squawks'] |
| # A dict of list of (card name, device) to be skipped on some boards. |
| _DEVICES_TO_BE_SKIPPED = { |
| # On the following boards, devices 4,5,6 are HDMI devices. |
| 'asuka': {'sklnau8825max': [4, 5, 6]}, |
| 'cave': {'sklnau8825max': [4, 5, 6]}, |
| 'snappy': {'bxtda7219max': [4, 5, 6]}, |
| # Chell's HDMI device 4 can not be used without being plugged. |
| # Also, its HDMI devices 5 and 6 are dummy devices. |
| 'chell': {'sklnau8825adi': [4, 5, 6]}, |
| # Kevin's device 3 is a DisplayPort device. |
| 'kevin': {'rk3399-gru-sound': [3]}, |
| } |
| |
| def run_once(self, to_test): |
| """Run alsa_api_test binary and verify its result. |
| |
| Checks the source code of alsa_api_test in audiotest repo for detail. |
| |
| @param to_test: support these test items: |
| move: Checks snd_pcm_forward API. |
| fill: Checks snd_pcm_mmap_begin API. |
| drop: Checks snd_pcm_drop API. |
| |
| """ |
| # Skip test_drop on boards that do not implement snd_pcm_drop |
| # correctly, as it cannot pass. |
| board = utils.get_board().lower() |
| if to_test == 'drop' and board in self._BOARDS_WITHOUT_DROP_SUPPORT: |
| logging.info('Skipping test_drop for unsupported board: %s', board) |
| return |
| |
| self._cardnames = alsa_utils.get_soundcard_names() |
| self._devices = [] |
| self._find_sound_devices() |
| method_name = '_test_' + to_test |
| method = getattr(self, method_name) |
| |
| # Stop CRAS to make sure the audio device won't be occupied. |
| utils.stop_service('cras', ignore_status=True) |
| |
| try: |
| for card_index, device_index in self._devices: |
| device = 'hw:%s,%s' % (card_index, device_index) |
| method(device) |
| finally: |
| # Restart CRAS. |
| utils.start_service('cras', ignore_status=True) |
| |
| |
| def _skip_device(self, card_device): |
| """Skips devices on some boards. |
| |
| @param card_device: A tuple of (card index, device index). |
| |
| @returns: True if the device should be skipped. False otherwise. |
| |
| """ |
| card_name = self._cardnames[card_device[0]] |
| |
| return card_device[1] in self._DEVICES_TO_BE_SKIPPED.get( |
| utils.get_board().lower(), {}).get(card_name, []) |
| |
| |
| def _find_sound_devices(self): |
| """Finds playback devices in sound device directory. |
| |
| @raises: error.TestError if there is no playback device. |
| """ |
| filenames = os.listdir(self._SND_DEV_DIR) |
| for filename in filenames: |
| search = re.match(self._PLAYBACK_DEVICE_NAME, filename) |
| if search: |
| card_device = (search.group(1), int(search.group(2))) |
| if not self._skip_device(card_device): |
| self._devices.append(card_device) |
| if not self._devices: |
| raise error.TestError('There is no playback device') |
| |
| |
| def _make_alsa_api_test_command(self, option, device): |
| """Makes command for alsa_api_test. |
| |
| @param option: same as to_test in run_once. |
| @param device: device in hw:<card index>:<device index> format. |
| |
| @returns: The command in a list of args. |
| |
| """ |
| return ['alsa_api_test', '--device', device, '--%s' % option] |
| |
| |
| def _test_move(self, device): |
| """Runs alsa_api_test command and checks the return code. |
| |
| Test snd_pcm_forward can move appl_ptr to hw_ptr. |
| |
| @param device: device in hw:<card index>:<device index> format. |
| |
| @raises error.TestError if command fails. |
| |
| """ |
| ret = utils.system( |
| command=self._make_alsa_api_test_command('move', device), |
| ignore_status=True) |
| if ret: |
| raise error.TestError( |
| 'ALSA API failed to move appl_ptr on device %s' % device) |
| |
| |
| def _test_fill(self, device): |
| """Runs alsa_api_test command and checks the return code. |
| |
| Test snd_pcm_mmap_begin can provide the access to the buffer, and memset |
| can fill it with zeros without using snd_pcm_mmap_commit. |
| |
| @param device: device in hw:<card index>:<device index> format. |
| |
| @raises error.TestError if command fails. |
| |
| """ |
| ret = utils.system( |
| command=self._make_alsa_api_test_command('fill', device), |
| ignore_status=True) |
| if ret: |
| raise error.TestError( |
| 'ALSA API failed to fill buffer on device %s' % device) |
| |
| |
| def _test_drop(self, device): |
| """Runs alsa_api_test command and checks the return code. |
| |
| Test snd_pcm_drop can stop playback and reset hw_ptr to 0 in hardware. |
| |
| @param device: device in hw:<card index>:<device index> format. |
| |
| @raises error.TestError if command fails. |
| |
| """ |
| ret = utils.system( |
| command=self._make_alsa_api_test_command('drop', device), |
| ignore_status=True) |
| if ret: |
| raise error.TestError( |
| 'ALSA API failed to drop playback and reset hw_ptr' |
| 'on device %s' % device) |