• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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