1ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch# Copyright (c) 2015 The Chromium OS Authors. All rights reserved.
2ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch# Use of this source code is governed by a BSD-style license that can be
3ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch# found in the LICENSE file.
4ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
5ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Brochimport collections
6ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Brochimport glob
7ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Brochimport logging
8ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Brochimport re
9ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Brochimport time
10ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
11ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Brochfrom autotest_lib.client.bin import test
12ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Brochfrom autotest_lib.client.common_lib import error, utils
13ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Brochfrom autotest_lib.client.cros import ec as cros_ec, cros_logging
14ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
15ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
16ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Brochclass usbpd_GFU(test.test):
17ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    """Integration test for USB-PD Google Firmware Update (GFU).
18ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
19ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    Test should:
20ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    - interrogate what firmware's are available for each device and for each:
21ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch      1. Use ectool's flashpd to write RW with that to mimic old hw
22ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch         - Validate that kernel driver successfully updates to latest RW.
23ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch      2. Erase RW and see update as well.
24ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
25ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    TODO:
26ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch      3. Check that update is checked after S2R.
27ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    """
28ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
29ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    version = 1
30ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
31ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    FW_PATH = '/lib/firmware/cros-pd'
32ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    # <device>_v<major>.<minor>.<build>-<commit SHA>
33ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    FW_NAME_RE = r'%s/(\w+)_v(\d+)\.(\d+)\.(\d+)-([0-9a-f]+).*' % (FW_PATH)
34ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    GOOGLE_VID = '0x18d1'
35ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    MAX_UPDATE_SECS = 80
36ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    FW_UP_DNAME = 'cros_ec_pd_update'
37ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    # TODO(tbroch) This will be change once cros_ec_pd_update is abstracted from
38ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    # ACPI driver.  Will need to fix this once it happens.
39ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    FW_UP_DISABLE_PATH = '/sys/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:1e/PNP0C09:00/GOOG0003:00/disable'
40ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
41ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    # TODO(tbroch) find better way to build this or we'll have to edit test for
42ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    # each new PD peripheral.
43ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    DEV_MAJOR = dict(zinger=1, minimuffin=2, dingdong=3, hoho=4)
44ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
45ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    def _index_firmware_avail(self):
46ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        """Index the various USB-PD firmwares in the rootfs.
47ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
48ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        TODO(crosbug.com/434522) This method will need reworked after we've come
49ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        up with a better method for firmware release.
50ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
51ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @returns: dictionary of firmwares (key == name, value == list of
52ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch          firmware paths)
53ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        """
54ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        fw_dict = collections.defaultdict(list)
55ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        for fw in glob.glob('%s/*_v[1-9].*.bin' % (self.FW_PATH)):
56ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            mat = re.match(self.FW_NAME_RE, fw)
57ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            if not mat:
58ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                continue
59ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
60ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            name = mat.group(1)
61ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            fw_dict[name].append(fw)
62ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
63ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        return fw_dict
64ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
65ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    def _is_gfu(self, port):
66ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        """Is it in GFU?
67ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
68ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @param port: EC_USBPD object for port.
69ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
70ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @returns: True if GFU enterd, False otherwise.
71ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        """
72ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        return port.is_amode_supported(self.GOOGLE_VID)
73ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
74ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    def _is_in_rw(self, port):
75ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        """Is PD device in RW firmware?
76ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
77ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @param port: EC_USBPD object for port.
78ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
79ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @returns: True if in RW, False otherwise.
80ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        """
81ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        flash_info = port.get_flash_info()
82ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        logging.debug('flash_info = %s', flash_info)
83ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        return flash_info['image_status'] == 'RW'
84ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
85ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    def _set_kernel_fw_update(self, disable=0):
86ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        """Disable the FW update driver.
87ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
88ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @param disable: 1 for disable, 0 for enable.
89ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        """
90ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        utils.write_one_line(self.FW_UP_DISABLE_PATH, disable)
91ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        if not disable:
92ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            # Allow kernel driver time quiesce
93ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            time.sleep(2)
94ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
95ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    def _modify_rw(self, port, rw=None, tries=3):
96ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        """Modify RW of USB-PD device in <port>.
97ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
98ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @param port: EC_USBPD object for port.
99ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @param rw: Path to RW FW to write using ectool.  If None then uses
100ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch          /dev/null to invalidate the RW.
101ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @param tries: Number of tries to update RW via flashpd
102ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
103ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @returns: True if success, False otherwise.
104ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        """
105ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        timeout = self.MAX_UPDATE_SECS
106ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
107ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        if not rw:
108ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            rw = '/dev/null'
109ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            tries = 1
110ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
111ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        self._set_kernel_fw_update(disable=1)
112ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
113ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        while (tries):
114ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            try:
115ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                # Note in flashpd <dev_major> <port> <file> the dev_major is
116ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                # unnecessary in all cases so its just been set to 0
117ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                port.ec_command('flashpd 0 %d %s' % (port.index, rw),
118ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                                ignore_status=True, timeout=timeout)
119ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
120ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            except error.CmdTimeoutError:
121ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                # TODO(tbroch) could remove try/except if ec_command used run
122ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                # instead of system_output + ignore_timeout=True
123ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                tries -= 1
124ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                continue
125ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
126ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            if rw != '/dev/null' and not self._is_in_rw(port):
127ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                logging.warn('Port%d: not in RW after flashpd ... retrying',
128ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                             port.index)
129ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                tries -= 1
130ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            else:
131ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                break
132ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
133ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        self._set_kernel_fw_update()
134ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
135ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        msg = self._reader.get_last_msg([r'%s.*is in RO' % port.index,
136ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                                         self.FW_UP_DNAME],
137ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                                        retries=5, sleep_seconds=2)
138ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        if not msg:
139ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            logging.warn('Port%d: Driver does NOT see dev in not in RO',
140ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                         port.index)
141ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            return False
142ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        logging.info('Port%d: Driver sees device in RO', port.index)
143ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        return True
144ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
145ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    def _test_update(self, port, rw=None, tries=3):
146ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        """Test RW update.
147ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
148ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        Method tests the kernel's RW update process by first modifying the
149ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        existing RW (either invalidating or rolling it back) via ectool.  It
150ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        then querys the syslog to validate kernel sees the need for update and
151ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        is successful.
152ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
153ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @param port: EC_USBPD object for port.
154ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @param rw: path to RW firmware to write via ectool to test upgrade.
155ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @param tries: integer number of attempts to write RW.  Necessary as
156ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch          update is not robust (design decision).
157ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        """
158ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        if not tries:
159ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            raise error.TestError('Retries must be > 0')
160ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
161ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        if not self._is_in_rw(port):
162ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            raise error.TestError('Port%d: Device is not in RW' % port.index)
163ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
164ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        fw_up_re = r'%s.*Port%d FW update completed' % (self.FW_UP_DNAME,
165ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                                                        port.index)
166ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
167ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        while tries:
168ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            self._reader.set_start_by_current()
169ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            rsp = self._modify_rw(port, rw)
170ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
171ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            if not rsp:
172ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                rsp_str = 'Port%d: RW modified with RW=%s failed' % \
173ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                          (port.index, rw)
174ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                if tries:
175ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                    logging.warn('%s ... retrying.', rsp_str)
176ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                    tries -= 1
177ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                else:
178ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                    raise error.TestError(rsp_str)
179ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
180ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            self._reader.set_start_by_current()
181ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            msg = self._reader.get_last_msg([fw_up_re],
182ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                                            retries=(self.MAX_UPDATE_SECS / 2),
183ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                                            sleep_seconds=2)
184ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
185ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            if not msg:
186ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                rsp_str = 'Port%d: driver did NOT update FW' % port.index
187ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                if tries:
188ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                    logging.warn('%s ... retrying.', rsp_str)
189ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                    tries -= 1
190ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                    continue
191ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                else:
192ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                    raise error.TestError(rsp_str)
193ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
194ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            logging.info('Port%d: Driver completed RW update', port.index)
195ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
196ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            # Allow adequate reboot time after RW write completes and device is
197ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            # rebooted.
198ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            time.sleep(3)
199ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
200ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            if not self._is_in_rw(port):
201ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                rsp_str = 'Port%d: Device is not in RW' % port.index
202ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                if tries:
203ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                    logging.warn('%s ... retrying.', rsp_str)
204ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                    tries -= 1
205ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                    continue
206ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                else:
207ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                    raise error.TestError(rsp_str)
208ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
209ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            break # success #
210ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
211ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    def _test_rw_rollback(self, port, fw_dict):
212ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        """Test rolling back RW firmware.
213ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
214ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @param port: EC_USBPD object for port.
215ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @param fw_dict: dictionary of firmwares.
216ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        """
217ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        self._set_kernel_fw_update()
218ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
219ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        # test old RW update
220ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        flash_info = port.get_flash_info()
221ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        for dev_name in fw_dict.keys():
222ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            if flash_info['dev_major'] == self.DEV_MAJOR[dev_name]:
223ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                for old_rw in sorted(fw_dict[dev_name], reverse=True)[1:]:
224ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                    logging.info('Port%d: Rollback test %s to %s',
225ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                                 port.index, dev_name, old_rw)
226ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                    self._test_update(port, rw=old_rw)
227ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                break
228ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
229ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    def _test_ro_only(self, port, ro_reps):
230ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        """Test FW update on device with RO only.
231ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
232ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @param port: EC_USBPD object for port.
233ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        @param ro_reps: Number of times to repeat test.
234ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        """
235ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        # test update in RO ro_reps times
236ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        for i in xrange(ro_reps):
237ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            logging.info('RO Loop%d', i)
238ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            self._test_update(port)
239ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
240ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    def run_once(self, ro_reps=1):
241ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
242ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        fw_dict = self._index_firmware_avail()
243ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
244ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        self._usbpd = cros_ec.EC_USBPD()
245ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        self._reader = cros_logging.LogReader()
246ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
247ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        for port in self._usbpd.ports:
248ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            if not port.is_dfp():
249ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                continue
250ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
251ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            logging.info('Port%d: is a DFP', port.index)
252ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
253ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            if not self._is_gfu(port):
254ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch                continue
255ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
256ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            logging.info('Port%d: supports GFU', port.index)
257ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
258ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            self._test_rw_rollback(port, fw_dict)
259ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch            self._test_ro_only(port, ro_reps)
260ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch
261ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch    def cleanup(self):
262ee1ba32ea5ba3bb84c1ed040dbc9a2976edcef43Todd Broch        self._set_kernel_fw_update()
263