Enabling Stout on FAFT

BUG=chrome-os-partner:13970
TEST=most FAFT tests pass when running control.faft_bios

Change-Id: I364919e4ff47ccc434b3396673e849e3bf9c1305
Reviewed-on: https://gerrit.chromium.org/gerrit/37944
Reviewed-by: Simran Basi <[email protected]>
Commit-Ready: Gediminas Ramanauskas <[email protected]>
Tested-by: Gediminas Ramanauskas <[email protected]>
diff --git a/server/cros/faft_client_attribute.py b/server/cros/faft_client_attribute.py
index bcc16fe..5b8ea41 100644
--- a/server/cros/faft_client_attribute.py
+++ b/server/cros/faft_client_attribute.py
@@ -3,11 +3,28 @@
 # found in the LICENSE file.
 
 class FAFTClientAttribute(object):
-    """Class that tests platform name and gives client machine attributes."""
+    """Class that tests platform name and gives client machine attributes.
+
+    Class attributes:
+      broken_warm_reset: boolean, True if warm_reset GPIO is not supported.
+            False otherwise.
+      broken_rec_mode: boolean, True if rec_mode GPIO is not supported.
+            False otherwise.
+      chrome_ec: boolean, True if ec is developed by chrome team.
+            False otherwise.
+      has_lid: boolean, True if the device has a lid. False otherwise.
+      has_keyboard: boolean, True if the device has a built in keyboard.
+            False otherwise.
+      ec_capability: list, specifies ec capability list.
+      wp_voltage: string, specifies write protect pin voltage.
+      key_matrix_layout: int, specifies which keyboard layout needs to be used
+            for testing.
+    """
     version = 1
 
     # Default settings
     broken_warm_reset = False
+    broken_rec_mode = False
     chrome_ec = False
     has_lid = True
     has_keyboard = True
@@ -26,9 +43,13 @@
         self.platform = platform
 
         # Set 'broken_warm_reset'
-        if platform in ['Parrot', 'Butterfly']:
+        if platform in ['Parrot', 'Butterfly', 'Stout']:
             self.broken_warm_reset = True
 
+        # Set 'broken_rec_mode' for Stout because it does not have rec_mode GPIO
+        if platform in ['Stout']:
+            self.broken_rec_mode = True
+
         # Set 'chrome_ec'
         if platform in ['Link', 'Snow']:
             self.chrome_ec = True
@@ -65,3 +86,7 @@
         # Set 'key_matrix_layout'
         if platform == 'Parrot':
             self.key_matrix_layout = 1
+
+        # Set 'key_matrix_layout'
+        if platform == 'Stout':
+            self.key_matrix_layout = 2
diff --git a/server/cros/faft_delay_constants.py b/server/cros/faft_delay_constants.py
index e1d806d..e3f1278 100644
--- a/server/cros/faft_delay_constants.py
+++ b/server/cros/faft_delay_constants.py
@@ -38,6 +38,8 @@
     devserver = 10
     # Delay of waiting factory install shim to reset TPM
     install_shim_done = 120
+    # Delay for user to power cycle the device
+    user_power_cycle = 20
 
     def __init__(self, platform=None):
         """Initialized.
diff --git a/server/cros/faftsequence.py b/server/cros/faftsequence.py
index c560814..23ac54b 100644
--- a/server/cros/faftsequence.py
+++ b/server/cros/faftsequence.py
@@ -18,6 +18,7 @@
 from autotest_lib.server.cros.faft_client_attribute import FAFTClientAttribute
 from autotest_lib.server.cros.faft_delay_constants import FAFTDelayConstants
 from autotest_lib.server.cros.servo_test import ServoTest
+from autotest_lib.server import hosts
 from autotest_lib.site_utils import lab_test
 from autotest_lib.site_utils.chromeos_test.common_util import ChromeOSTestError
 
@@ -175,6 +176,8 @@
             # Setting up key matrix mapping
             self.servo.set_key_matrix(self.client_attr.key_matrix_layout)
 
+            self._host = hosts.create_host(self.servo.get_target_hostname())
+
 
     def setup(self, ec_wp=None):
         """Autotest setup function."""
@@ -875,7 +878,8 @@
                                  vboot.GBB_FLAG_FORCE_DEV_SWITCH_ON |
                                  vboot.GBB_FLAG_FORCE_DEV_BOOT_USB |
                                  vboot.GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK,
-                                 vboot.GBB_FLAG_ENTER_TRIGGERS_TONORM)
+                                 vboot.GBB_FLAG_ENTER_TRIGGERS_TONORM |
+                                 vboot.GBB_FLAG_FAFT_KEY_OVERIDE)
         self.mark_setup_done('gbb_flags')
 
 
@@ -922,6 +926,15 @@
             time.sleep(self.delay.ec_boot_to_console)
             self.ec.set_hostevent(chrome_ec.HOSTEVENT_KEYBOARD_RECOVERY)
             self.servo.power_short_press()
+        elif self.client_attr.broken_rec_mode:
+            if self._host.has_power():
+                self._host.power_cycle()
+            else:
+                logging.info('You have %d seconds to power cycle this device.',
+                             self.delay.user_power_cycle)
+                time.sleep(self.delay.user_power_cycle)
+            logging.info('Booting to recovery mode.')
+            self.servo.custom_recovery_mode()
         else:
             self.servo.enable_recovery_mode()
             self.cold_reboot()
@@ -976,7 +989,8 @@
 
     def disable_keyboard_dev_mode(self):
         logging.info("Disabling keyboard controlled developer mode")
-        if not self.client_attr.chrome_ec:
+        if (not self.client_attr.chrome_ec and
+            not self.client_attr.broken_rec_mode):
             self.servo.disable_recovery_mode()
         self.cold_reboot()
         self.wait_for_client_offline()
diff --git a/server/cros/servo.py b/server/cros/servo.py
index bba32b1..1ba4c5e 100644
--- a/server/cros/servo.py
+++ b/server/cros/servo.py
@@ -81,7 +81,19 @@
         'ctrl':          ['1', '1', '1', '0'],
         'none':          ['1', '1', '1', '1']}
 
-    KEY_MATRIX = [KEY_MATRIX_ALT_0, KEY_MATRIX_ALT_1]
+    KEY_MATRIX_ALT_2 = {
+        'ctrl_d':        ['0', '0', '0', '1'],
+        'd':             ['0', '0', '1', '1'],
+        'unused':        ['0', '1', '1', '1'],
+        'rec_mode':      ['1', '0', '0', '0'],
+        'ctrl_enter':    ['1', '0', '0', '1'],
+        'enter':         ['1', '0', '1', '1'],
+        'ctrl':          ['1', '1', '0', '1'],
+        'refresh':       ['1', '1', '1', '0'],
+        'ctrl_refresh':  ['1', '1', '1', '1'],
+        'none':          ['1', '1', '1', '1']}
+
+    KEY_MATRIX = [KEY_MATRIX_ALT_0, KEY_MATRIX_ALT_1, KEY_MATRIX_ALT_2]
 
     @staticmethod
     def _make_servo_hostname(hostname):
@@ -104,7 +116,7 @@
         servo_host = Servo._make_servo_hostname(target_hostname)
         if utils.host_is_in_lab_zone(servo_host):
             try:
-                return Servo(servo_host=servo_host)
+                return Servo(servo_host=servo_host, target_host=target_hostname)
             except: # pylint: disable=W0702
                 # TODO(jrbarnette):  Long-term, if we can't get to
                 # a servo in the lab, we want to fail, so we should
@@ -114,17 +126,25 @@
         return None
 
 
-    def __init__(self, servo_host='localhost', servo_port=9999):
+    def __init__(self, servo_host='localhost', target_host='local',
+                 servo_port=9999):
         """Sets up the servo communication infrastructure.
 
-        @param servo_host Name of the host where the servod process
-                          is running.
-        @param servo_port Port the servod process is listening on.
+        @param servo_host  Name of the host where the servod process
+                           is running.
+        @param target_host Name of the target which is connected to servo
+        @param servo_port  Port the servod process is listening on.
         """
         self._key_matrix = 0
         self._server = None
         self._connect_servod(servo_host, servo_port)
         self._is_localhost = (servo_host == 'localhost')
+        self._target_host = target_host
+
+
+    def get_target_hostname(self):
+        """Retrieves target (DUT) hostname."""
+        return self._target_host
 
 
     def initialize_dut(self, cold_reset=False):
@@ -218,15 +238,24 @@
         time.sleep(Servo.SLEEP_DELAY)
 
 
-    def _press_and_release_keys(self, key,
-                                press_secs=SERVO_KEY_PRESS_DELAY):
-        """Simulate button presses."""
+    def _press_keys(self, key):
+        """Simulate button presses.
+
+        Note, key presses will remain on indefinitely. See
+            _press_and_release_keys for release procedure.
+        """
         (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix][key]
         self.set_nocheck('kbd_m2_a0', m2_a0)
         self.set_nocheck('kbd_m2_a1', m2_a1)
         self.set_nocheck('kbd_m1_a0', m1_a0)
         self.set_nocheck('kbd_m1_a1', m1_a1)
         self.set_nocheck('kbd_en', 'on')
+
+
+    def _press_and_release_keys(self, key,
+                                press_secs=SERVO_KEY_PRESS_DELAY):
+        """Simulate button presses and release."""
+        self._press_keys(key)
         time.sleep(press_secs)
         self.set_nocheck('kbd_en', 'off')
 
@@ -287,6 +316,14 @@
         self.set('rec_mode', 'on')
 
 
+    def custom_recovery_mode(self):
+        """Custom key combination to enter recovery mode."""
+        self._press_keys('rec_mode')
+        self.power_normal_press()
+        time.sleep(self.SERVO_KEY_PRESS_DELAY)
+        self.set_nocheck('kbd_en', 'off')
+
+
     def disable_recovery_mode(self):
         """Disable recovery mode on device."""
         self.set('rec_mode', 'off')
diff --git a/server/cros/vboot_constants.py b/server/cros/vboot_constants.py
index 03df6b0..71902ec 100644
--- a/server/cros/vboot_constants.py
+++ b/server/cros/vboot_constants.py
@@ -105,6 +105,8 @@
 GBB_FLAG_FORCE_DEV_BOOT_USB        = 0x00000010
 GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK = 0x00000020
 GBB_FLAG_ENTER_TRIGGERS_TONORM     = 0x00000040
+GBB_FLAG_FORCE_DEV_BOOT_LEGACY     = 0x00000080
+GBB_FLAG_FAFT_KEY_OVERIDE          = 0x00000100
 
 # VbSharedData flags, copied from:
 #     vboot_reference/firmware/include/vboot_struct.h