xref: /OK3568_Linux_fs/u-boot/test/py/u_boot_console_base.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun# Copyright (c) 2015 Stephen Warren
2*4882a593Smuzhiyun# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
3*4882a593Smuzhiyun#
4*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun# Common logic to interact with U-Boot via the console. This class provides
7*4882a593Smuzhiyun# the interface that tests use to execute U-Boot shell commands and wait for
8*4882a593Smuzhiyun# their results. Sub-classes exist to perform board-type-specific setup
9*4882a593Smuzhiyun# operations, such as spawning a sub-process for Sandbox, or attaching to the
10*4882a593Smuzhiyun# serial console of real hardware.
11*4882a593Smuzhiyun
12*4882a593Smuzhiyunimport multiplexed_log
13*4882a593Smuzhiyunimport os
14*4882a593Smuzhiyunimport pytest
15*4882a593Smuzhiyunimport re
16*4882a593Smuzhiyunimport sys
17*4882a593Smuzhiyunimport u_boot_spawn
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun# Regexes for text we expect U-Boot to send to the console.
20*4882a593Smuzhiyunpattern_u_boot_spl_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}[^\r\n]*\\))')
21*4882a593Smuzhiyunpattern_u_boot_main_signon = re.compile('(U-Boot \\d{4}\\.\\d{2}[^\r\n]*\\))')
22*4882a593Smuzhiyunpattern_stop_autoboot_prompt = re.compile('Hit any key to stop autoboot: ')
23*4882a593Smuzhiyunpattern_unknown_command = re.compile('Unknown command \'.*\' - try \'help\'')
24*4882a593Smuzhiyunpattern_error_notification = re.compile('## Error: ')
25*4882a593Smuzhiyunpattern_error_please_reset = re.compile('### ERROR ### Please RESET the board ###')
26*4882a593Smuzhiyun
27*4882a593SmuzhiyunPAT_ID = 0
28*4882a593SmuzhiyunPAT_RE = 1
29*4882a593Smuzhiyun
30*4882a593Smuzhiyunbad_pattern_defs = (
31*4882a593Smuzhiyun    ('spl_signon', pattern_u_boot_spl_signon),
32*4882a593Smuzhiyun    ('main_signon', pattern_u_boot_main_signon),
33*4882a593Smuzhiyun    ('stop_autoboot_prompt', pattern_stop_autoboot_prompt),
34*4882a593Smuzhiyun    ('unknown_command', pattern_unknown_command),
35*4882a593Smuzhiyun    ('error_notification', pattern_error_notification),
36*4882a593Smuzhiyun    ('error_please_reset', pattern_error_please_reset),
37*4882a593Smuzhiyun)
38*4882a593Smuzhiyun
39*4882a593Smuzhiyunclass ConsoleDisableCheck(object):
40*4882a593Smuzhiyun    """Context manager (for Python's with statement) that temporarily disables
41*4882a593Smuzhiyun    the specified console output error check. This is useful when deliberately
42*4882a593Smuzhiyun    executing a command that is known to trigger one of the error checks, in
43*4882a593Smuzhiyun    order to test that the error condition is actually raised. This class is
44*4882a593Smuzhiyun    used internally by ConsoleBase::disable_check(); it is not intended for
45*4882a593Smuzhiyun    direct usage."""
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun    def __init__(self, console, check_type):
48*4882a593Smuzhiyun        self.console = console
49*4882a593Smuzhiyun        self.check_type = check_type
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun    def __enter__(self):
52*4882a593Smuzhiyun        self.console.disable_check_count[self.check_type] += 1
53*4882a593Smuzhiyun        self.console.eval_bad_patterns()
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun    def __exit__(self, extype, value, traceback):
56*4882a593Smuzhiyun        self.console.disable_check_count[self.check_type] -= 1
57*4882a593Smuzhiyun        self.console.eval_bad_patterns()
58*4882a593Smuzhiyun
59*4882a593Smuzhiyunclass ConsoleSetupTimeout(object):
60*4882a593Smuzhiyun    """Context manager (for Python's with statement) that temporarily sets up
61*4882a593Smuzhiyun    timeout for specific command. This is useful when execution time is greater
62*4882a593Smuzhiyun    then default 30s."""
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun    def __init__(self, console, timeout):
65*4882a593Smuzhiyun        self.p = console.p
66*4882a593Smuzhiyun        self.orig_timeout = self.p.timeout
67*4882a593Smuzhiyun        self.p.timeout = timeout
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun    def __enter__(self):
70*4882a593Smuzhiyun        return self
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun    def __exit__(self, extype, value, traceback):
73*4882a593Smuzhiyun        self.p.timeout = self.orig_timeout
74*4882a593Smuzhiyun
75*4882a593Smuzhiyunclass ConsoleBase(object):
76*4882a593Smuzhiyun    """The interface through which test functions interact with the U-Boot
77*4882a593Smuzhiyun    console. This primarily involves executing shell commands, capturing their
78*4882a593Smuzhiyun    results, and checking for common error conditions. Some common utilities
79*4882a593Smuzhiyun    are also provided too."""
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun    def __init__(self, log, config, max_fifo_fill):
82*4882a593Smuzhiyun        """Initialize a U-Boot console connection.
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun        Can only usefully be called by sub-classes.
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun        Args:
87*4882a593Smuzhiyun            log: A mulptiplex_log.Logfile object, to which the U-Boot output
88*4882a593Smuzhiyun                will be logged.
89*4882a593Smuzhiyun            config: A configuration data structure, as built by conftest.py.
90*4882a593Smuzhiyun            max_fifo_fill: The maximum number of characters to send to U-Boot
91*4882a593Smuzhiyun                command-line before waiting for U-Boot to echo the characters
92*4882a593Smuzhiyun                back. For UART-based HW without HW flow control, this value
93*4882a593Smuzhiyun                should be set less than the UART RX FIFO size to avoid
94*4882a593Smuzhiyun                overflow, assuming that U-Boot can't keep up with full-rate
95*4882a593Smuzhiyun                traffic at the baud rate.
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun        Returns:
98*4882a593Smuzhiyun            Nothing.
99*4882a593Smuzhiyun        """
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun        self.log = log
102*4882a593Smuzhiyun        self.config = config
103*4882a593Smuzhiyun        self.max_fifo_fill = max_fifo_fill
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun        self.logstream = self.log.get_stream('console', sys.stdout)
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun        # Array slice removes leading/trailing quotes
108*4882a593Smuzhiyun        self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
109*4882a593Smuzhiyun        self.prompt_compiled = re.compile('^' + re.escape(self.prompt), re.MULTILINE)
110*4882a593Smuzhiyun        self.p = None
111*4882a593Smuzhiyun        self.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
112*4882a593Smuzhiyun        self.eval_bad_patterns()
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun        self.at_prompt = False
115*4882a593Smuzhiyun        self.at_prompt_logevt = None
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun    def eval_bad_patterns(self):
118*4882a593Smuzhiyun        self.bad_patterns = [pat[PAT_RE] for pat in bad_pattern_defs \
119*4882a593Smuzhiyun            if self.disable_check_count[pat[PAT_ID]] == 0]
120*4882a593Smuzhiyun        self.bad_pattern_ids = [pat[PAT_ID] for pat in bad_pattern_defs \
121*4882a593Smuzhiyun            if self.disable_check_count[pat[PAT_ID]] == 0]
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun    def close(self):
124*4882a593Smuzhiyun        """Terminate the connection to the U-Boot console.
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun        This function is only useful once all interaction with U-Boot is
127*4882a593Smuzhiyun        complete. Once this function is called, data cannot be sent to or
128*4882a593Smuzhiyun        received from U-Boot.
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun        Args:
131*4882a593Smuzhiyun            None.
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun        Returns:
134*4882a593Smuzhiyun            Nothing.
135*4882a593Smuzhiyun        """
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun        if self.p:
138*4882a593Smuzhiyun            self.p.close()
139*4882a593Smuzhiyun        self.logstream.close()
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun    def run_command(self, cmd, wait_for_echo=True, send_nl=True,
142*4882a593Smuzhiyun            wait_for_prompt=True):
143*4882a593Smuzhiyun        """Execute a command via the U-Boot console.
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun        The command is always sent to U-Boot.
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun        U-Boot echoes any command back to its output, and this function
148*4882a593Smuzhiyun        typically waits for that to occur. The wait can be disabled by setting
149*4882a593Smuzhiyun        wait_for_echo=False, which is useful e.g. when sending CTRL-C to
150*4882a593Smuzhiyun        interrupt a long-running command such as "ums".
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun        Command execution is typically triggered by sending a newline
153*4882a593Smuzhiyun        character. This can be disabled by setting send_nl=False, which is
154*4882a593Smuzhiyun        also useful when sending CTRL-C.
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun        This function typically waits for the command to finish executing, and
157*4882a593Smuzhiyun        returns the console output that it generated. This can be disabled by
158*4882a593Smuzhiyun        setting wait_for_prompt=False, which is useful when invoking a long-
159*4882a593Smuzhiyun        running command such as "ums".
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun        Args:
162*4882a593Smuzhiyun            cmd: The command to send.
163*4882a593Smuzhiyun            wait_for_echo: Boolean indicating whether to wait for U-Boot to
164*4882a593Smuzhiyun                echo the command text back to its output.
165*4882a593Smuzhiyun            send_nl: Boolean indicating whether to send a newline character
166*4882a593Smuzhiyun                after the command string.
167*4882a593Smuzhiyun            wait_for_prompt: Boolean indicating whether to wait for the
168*4882a593Smuzhiyun                command prompt to be sent by U-Boot. This typically occurs
169*4882a593Smuzhiyun                immediately after the command has been executed.
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun        Returns:
172*4882a593Smuzhiyun            If wait_for_prompt == False:
173*4882a593Smuzhiyun                Nothing.
174*4882a593Smuzhiyun            Else:
175*4882a593Smuzhiyun                The output from U-Boot during command execution. In other
176*4882a593Smuzhiyun                words, the text U-Boot emitted between the point it echod the
177*4882a593Smuzhiyun                command string and emitted the subsequent command prompts.
178*4882a593Smuzhiyun        """
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun        if self.at_prompt and \
181*4882a593Smuzhiyun                self.at_prompt_logevt != self.logstream.logfile.cur_evt:
182*4882a593Smuzhiyun            self.logstream.write(self.prompt, implicit=True)
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun        try:
185*4882a593Smuzhiyun            self.at_prompt = False
186*4882a593Smuzhiyun            if send_nl:
187*4882a593Smuzhiyun                cmd += '\n'
188*4882a593Smuzhiyun            while cmd:
189*4882a593Smuzhiyun                # Limit max outstanding data, so UART FIFOs don't overflow
190*4882a593Smuzhiyun                chunk = cmd[:self.max_fifo_fill]
191*4882a593Smuzhiyun                cmd = cmd[self.max_fifo_fill:]
192*4882a593Smuzhiyun                self.p.send(chunk)
193*4882a593Smuzhiyun                if not wait_for_echo:
194*4882a593Smuzhiyun                    continue
195*4882a593Smuzhiyun                chunk = re.escape(chunk)
196*4882a593Smuzhiyun                chunk = chunk.replace('\\\n', '[\r\n]')
197*4882a593Smuzhiyun                m = self.p.expect([chunk] + self.bad_patterns)
198*4882a593Smuzhiyun                if m != 0:
199*4882a593Smuzhiyun                    self.at_prompt = False
200*4882a593Smuzhiyun                    raise Exception('Bad pattern found on console: ' +
201*4882a593Smuzhiyun                                    self.bad_pattern_ids[m - 1])
202*4882a593Smuzhiyun            if not wait_for_prompt:
203*4882a593Smuzhiyun                return
204*4882a593Smuzhiyun            m = self.p.expect([self.prompt_compiled] + self.bad_patterns)
205*4882a593Smuzhiyun            if m != 0:
206*4882a593Smuzhiyun                self.at_prompt = False
207*4882a593Smuzhiyun                raise Exception('Bad pattern found on console: ' +
208*4882a593Smuzhiyun                                self.bad_pattern_ids[m - 1])
209*4882a593Smuzhiyun            self.at_prompt = True
210*4882a593Smuzhiyun            self.at_prompt_logevt = self.logstream.logfile.cur_evt
211*4882a593Smuzhiyun            # Only strip \r\n; space/TAB might be significant if testing
212*4882a593Smuzhiyun            # indentation.
213*4882a593Smuzhiyun            return self.p.before.strip('\r\n')
214*4882a593Smuzhiyun        except Exception as ex:
215*4882a593Smuzhiyun            self.log.error(str(ex))
216*4882a593Smuzhiyun            self.cleanup_spawn()
217*4882a593Smuzhiyun            raise
218*4882a593Smuzhiyun        finally:
219*4882a593Smuzhiyun            self.log.timestamp()
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun    def run_command_list(self, cmds):
222*4882a593Smuzhiyun        """Run a list of commands.
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun        This is a helper function to call run_command() with default arguments
225*4882a593Smuzhiyun        for each command in a list.
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun        Args:
228*4882a593Smuzhiyun            cmd: List of commands (each a string).
229*4882a593Smuzhiyun        Returns:
230*4882a593Smuzhiyun            A list of output strings from each command, one element for each
231*4882a593Smuzhiyun            command.
232*4882a593Smuzhiyun        """
233*4882a593Smuzhiyun        output = []
234*4882a593Smuzhiyun        for cmd in cmds:
235*4882a593Smuzhiyun            output.append(self.run_command(cmd))
236*4882a593Smuzhiyun        return output
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun    def ctrlc(self):
239*4882a593Smuzhiyun        """Send a CTRL-C character to U-Boot.
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun        This is useful in order to stop execution of long-running synchronous
242*4882a593Smuzhiyun        commands such as "ums".
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun        Args:
245*4882a593Smuzhiyun            None.
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun        Returns:
248*4882a593Smuzhiyun            Nothing.
249*4882a593Smuzhiyun        """
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun        self.log.action('Sending Ctrl-C')
252*4882a593Smuzhiyun        self.run_command(chr(3), wait_for_echo=False, send_nl=False)
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun    def wait_for(self, text):
255*4882a593Smuzhiyun        """Wait for a pattern to be emitted by U-Boot.
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun        This is useful when a long-running command such as "dfu" is executing,
258*4882a593Smuzhiyun        and it periodically emits some text that should show up at a specific
259*4882a593Smuzhiyun        location in the log file.
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun        Args:
262*4882a593Smuzhiyun            text: The text to wait for; either a string (containing raw text,
263*4882a593Smuzhiyun                not a regular expression) or an re object.
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun        Returns:
266*4882a593Smuzhiyun            Nothing.
267*4882a593Smuzhiyun        """
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun        if type(text) == type(''):
270*4882a593Smuzhiyun            text = re.escape(text)
271*4882a593Smuzhiyun        m = self.p.expect([text] + self.bad_patterns)
272*4882a593Smuzhiyun        if m != 0:
273*4882a593Smuzhiyun            raise Exception('Bad pattern found on console: ' +
274*4882a593Smuzhiyun                            self.bad_pattern_ids[m - 1])
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun    def drain_console(self):
277*4882a593Smuzhiyun        """Read from and log the U-Boot console for a short time.
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun        U-Boot's console output is only logged when the test code actively
280*4882a593Smuzhiyun        waits for U-Boot to emit specific data. There are cases where tests
281*4882a593Smuzhiyun        can fail without doing this. For example, if a test asks U-Boot to
282*4882a593Smuzhiyun        enable USB device mode, then polls until a host-side device node
283*4882a593Smuzhiyun        exists. In such a case, it is useful to log U-Boot's console output
284*4882a593Smuzhiyun        in case U-Boot printed clues as to why the host-side even did not
285*4882a593Smuzhiyun        occur. This function will do that.
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun        Args:
288*4882a593Smuzhiyun            None.
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun        Returns:
291*4882a593Smuzhiyun            Nothing.
292*4882a593Smuzhiyun        """
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun        # If we are already not connected to U-Boot, there's nothing to drain.
295*4882a593Smuzhiyun        # This should only happen when a previous call to run_command() or
296*4882a593Smuzhiyun        # wait_for() failed (and hence the output has already been logged), or
297*4882a593Smuzhiyun        # the system is shutting down.
298*4882a593Smuzhiyun        if not self.p:
299*4882a593Smuzhiyun            return
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun        orig_timeout = self.p.timeout
302*4882a593Smuzhiyun        try:
303*4882a593Smuzhiyun            # Drain the log for a relatively short time.
304*4882a593Smuzhiyun            self.p.timeout = 1000
305*4882a593Smuzhiyun            # Wait for something U-Boot will likely never send. This will
306*4882a593Smuzhiyun            # cause the console output to be read and logged.
307*4882a593Smuzhiyun            self.p.expect(['This should never match U-Boot output'])
308*4882a593Smuzhiyun        except u_boot_spawn.Timeout:
309*4882a593Smuzhiyun            pass
310*4882a593Smuzhiyun        finally:
311*4882a593Smuzhiyun            self.p.timeout = orig_timeout
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun    def ensure_spawned(self):
314*4882a593Smuzhiyun        """Ensure a connection to a correctly running U-Boot instance.
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun        This may require spawning a new Sandbox process or resetting target
317*4882a593Smuzhiyun        hardware, as defined by the implementation sub-class.
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun        This is an internal function and should not be called directly.
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun        Args:
322*4882a593Smuzhiyun            None.
323*4882a593Smuzhiyun
324*4882a593Smuzhiyun        Returns:
325*4882a593Smuzhiyun            Nothing.
326*4882a593Smuzhiyun        """
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun        if self.p:
329*4882a593Smuzhiyun            return
330*4882a593Smuzhiyun        try:
331*4882a593Smuzhiyun            self.log.start_section('Starting U-Boot')
332*4882a593Smuzhiyun            self.at_prompt = False
333*4882a593Smuzhiyun            self.p = self.get_spawn()
334*4882a593Smuzhiyun            # Real targets can take a long time to scroll large amounts of
335*4882a593Smuzhiyun            # text if LCD is enabled. This value may need tweaking in the
336*4882a593Smuzhiyun            # future, possibly per-test to be optimal. This works for 'help'
337*4882a593Smuzhiyun            # on board 'seaboard'.
338*4882a593Smuzhiyun            if not self.config.gdbserver:
339*4882a593Smuzhiyun                self.p.timeout = 30000
340*4882a593Smuzhiyun            self.p.logfile_read = self.logstream
341*4882a593Smuzhiyun            bcfg = self.config.buildconfig
342*4882a593Smuzhiyun            config_spl = bcfg.get('config_spl', 'n') == 'y'
343*4882a593Smuzhiyun            config_spl_serial_support = bcfg.get('config_spl_serial_support',
344*4882a593Smuzhiyun                                                 'n') == 'y'
345*4882a593Smuzhiyun            env_spl_skipped = self.config.env.get('env__spl_skipped',
346*4882a593Smuzhiyun                                                  False)
347*4882a593Smuzhiyun            if config_spl and config_spl_serial_support and not env_spl_skipped:
348*4882a593Smuzhiyun                m = self.p.expect([pattern_u_boot_spl_signon] +
349*4882a593Smuzhiyun                                  self.bad_patterns)
350*4882a593Smuzhiyun                if m != 0:
351*4882a593Smuzhiyun                    raise Exception('Bad pattern found on SPL console: ' +
352*4882a593Smuzhiyun                                    self.bad_pattern_ids[m - 1])
353*4882a593Smuzhiyun            m = self.p.expect([pattern_u_boot_main_signon] + self.bad_patterns)
354*4882a593Smuzhiyun            if m != 0:
355*4882a593Smuzhiyun                raise Exception('Bad pattern found on console: ' +
356*4882a593Smuzhiyun                                self.bad_pattern_ids[m - 1])
357*4882a593Smuzhiyun            self.u_boot_version_string = self.p.after
358*4882a593Smuzhiyun            while True:
359*4882a593Smuzhiyun                m = self.p.expect([self.prompt_compiled,
360*4882a593Smuzhiyun                    pattern_stop_autoboot_prompt] + self.bad_patterns)
361*4882a593Smuzhiyun                if m == 0:
362*4882a593Smuzhiyun                    break
363*4882a593Smuzhiyun                if m == 1:
364*4882a593Smuzhiyun                    self.p.send(' ')
365*4882a593Smuzhiyun                    continue
366*4882a593Smuzhiyun                raise Exception('Bad pattern found on console: ' +
367*4882a593Smuzhiyun                                self.bad_pattern_ids[m - 2])
368*4882a593Smuzhiyun            self.at_prompt = True
369*4882a593Smuzhiyun            self.at_prompt_logevt = self.logstream.logfile.cur_evt
370*4882a593Smuzhiyun        except Exception as ex:
371*4882a593Smuzhiyun            self.log.error(str(ex))
372*4882a593Smuzhiyun            self.cleanup_spawn()
373*4882a593Smuzhiyun            raise
374*4882a593Smuzhiyun        finally:
375*4882a593Smuzhiyun            self.log.timestamp()
376*4882a593Smuzhiyun            self.log.end_section('Starting U-Boot')
377*4882a593Smuzhiyun
378*4882a593Smuzhiyun    def cleanup_spawn(self):
379*4882a593Smuzhiyun        """Shut down all interaction with the U-Boot instance.
380*4882a593Smuzhiyun
381*4882a593Smuzhiyun        This is used when an error is detected prior to re-establishing a
382*4882a593Smuzhiyun        connection with a fresh U-Boot instance.
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun        This is an internal function and should not be called directly.
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun        Args:
387*4882a593Smuzhiyun            None.
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun        Returns:
390*4882a593Smuzhiyun            Nothing.
391*4882a593Smuzhiyun        """
392*4882a593Smuzhiyun
393*4882a593Smuzhiyun        try:
394*4882a593Smuzhiyun            if self.p:
395*4882a593Smuzhiyun                self.p.close()
396*4882a593Smuzhiyun        except:
397*4882a593Smuzhiyun            pass
398*4882a593Smuzhiyun        self.p = None
399*4882a593Smuzhiyun
400*4882a593Smuzhiyun    def restart_uboot(self):
401*4882a593Smuzhiyun        """Shut down and restart U-Boot."""
402*4882a593Smuzhiyun        self.cleanup_spawn()
403*4882a593Smuzhiyun        self.ensure_spawned()
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun    def get_spawn_output(self):
406*4882a593Smuzhiyun        """Return the start-up output from U-Boot
407*4882a593Smuzhiyun
408*4882a593Smuzhiyun        Returns:
409*4882a593Smuzhiyun            The output produced by ensure_spawed(), as a string.
410*4882a593Smuzhiyun        """
411*4882a593Smuzhiyun        if self.p:
412*4882a593Smuzhiyun            return self.p.get_expect_output()
413*4882a593Smuzhiyun        return None
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun    def validate_version_string_in_text(self, text):
416*4882a593Smuzhiyun        """Assert that a command's output includes the U-Boot signon message.
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun        This is primarily useful for validating the "version" command without
419*4882a593Smuzhiyun        duplicating the signon text regex in a test function.
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun        Args:
422*4882a593Smuzhiyun            text: The command output text to check.
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun        Returns:
425*4882a593Smuzhiyun            Nothing. An exception is raised if the validation fails.
426*4882a593Smuzhiyun        """
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun        assert(self.u_boot_version_string in text)
429*4882a593Smuzhiyun
430*4882a593Smuzhiyun    def disable_check(self, check_type):
431*4882a593Smuzhiyun        """Temporarily disable an error check of U-Boot's output.
432*4882a593Smuzhiyun
433*4882a593Smuzhiyun        Create a new context manager (for use with the "with" statement) which
434*4882a593Smuzhiyun        temporarily disables a particular console output error check.
435*4882a593Smuzhiyun
436*4882a593Smuzhiyun        Args:
437*4882a593Smuzhiyun            check_type: The type of error-check to disable. Valid values may
438*4882a593Smuzhiyun            be found in self.disable_check_count above.
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun        Returns:
441*4882a593Smuzhiyun            A context manager object.
442*4882a593Smuzhiyun        """
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun        return ConsoleDisableCheck(self, check_type)
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun    def temporary_timeout(self, timeout):
447*4882a593Smuzhiyun        """Temporarily set up different timeout for commands.
448*4882a593Smuzhiyun
449*4882a593Smuzhiyun        Create a new context manager (for use with the "with" statement) which
450*4882a593Smuzhiyun        temporarily change timeout.
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun        Args:
453*4882a593Smuzhiyun            timeout: Time in milliseconds.
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun        Returns:
456*4882a593Smuzhiyun            A context manager object.
457*4882a593Smuzhiyun        """
458*4882a593Smuzhiyun
459*4882a593Smuzhiyun        return ConsoleSetupTimeout(self, timeout)
460