faft: Implement Jetstream ModeSwitcher logic

This change adds the Jetstream ModeSwitcher implementation and
uses the new jetstream FAFT config as parent.

BUG=chrome-os-partner:16231,chrome-os-partner:39744
TEST=Ran suite:faft_lv1 and suite:faft_lv2 on Whirlwind. Some tests
failed and will be fixed by some future CLs.

Change-Id: I23c71ba86889a132cb54c7be169b0c9010880052
Reviewed-on: https://chromium-review.googlesource.com/272432
Tested-by: Tom Tam <waihong@google.com>
Reviewed-by: Yusuf Mohsinally <mohsinally@chromium.org>
Commit-Queue: Tom Tam <waihong@google.com>
diff --git a/server/cros/faft/config/jetstream.py b/server/cros/faft/config/jetstream.py
new file mode 100644
index 0000000..33599c2
--- /dev/null
+++ b/server/cros/faft/config/jetstream.py
@@ -0,0 +1,17 @@
+# Copyright 2015 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.
+
+"""FAFT configuration overrides for JetStream."""
+
+
+class Values(object):
+    """FAFT config values for JetStream."""
+
+    mode_switcher_type = 'jetstream_switcher'
+    fw_bypasser_type = 'jetstream_bypasser'
+
+    has_lid = False
+    has_keyboard = False
+    keyboard_dev = False
+    rec_button_dev_switch = True
diff --git a/server/cros/faft/config/storm.py b/server/cros/faft/config/storm.py
index bee346c..e97c5fb 100644
--- a/server/cros/faft/config/storm.py
+++ b/server/cros/faft/config/storm.py
@@ -4,10 +4,9 @@
 
 """FAFT configuration overrides for Storm."""
 
+from autotest_lib.server.cros.faft.config import jetstream
 
-class Values(object):
-    """FAFT config values for Storm."""
-    has_lid = False
-    has_keyboard = False
-    keyboard_dev = False
-    rec_button_dev_switch = True
+
+class Values(jetstream.Values):
+    """Inherit overrides from Jetstream."""
+    pass
diff --git a/server/cros/faft/config/whirlwind.py b/server/cros/faft/config/whirlwind.py
new file mode 100644
index 0000000..85f7744
--- /dev/null
+++ b/server/cros/faft/config/whirlwind.py
@@ -0,0 +1,12 @@
+# Copyright 2015 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.
+
+"""FAFT configuration overrides for Whirlwind."""
+
+from autotest_lib.server.cros.faft.config import jetstream
+
+
+class Values(jetstream.Values):
+    """Inherit overrides from Jetstream."""
+    pass
diff --git a/server/cros/faft/utils/mode_switcher.py b/server/cros/faft/utils/mode_switcher.py
index e1dfd8c..68015da 100644
--- a/server/cros/faft/utils/mode_switcher.py
+++ b/server/cros/faft/utils/mode_switcher.py
@@ -102,6 +102,52 @@
         self.servo.enter_key()
 
 
+class _JetstreamBypasser(_BaseFwBypasser):
+    """Controls bypass logic of Jetstream devices."""
+
+    def bypass_dev_mode(self):
+        """Bypass the dev mode firmware logic to boot internal image."""
+        # Jetstream does nothing to bypass.
+        pass
+
+
+    def bypass_dev_boot_usb(self):
+        """Bypass the dev mode firmware logic to boot USB."""
+        # TODO: Confirm if it is a proper way to trigger dev boot USB.
+        # We can't verify it this time due to a bug that always boots into
+        # USB on dev mode.
+        self.servo.enable_development_mode()
+        self.servo.switch_usbkey('dut')
+        time.sleep(self.faft_config.firmware_screen)
+        self.servo.toggle_development_switch()
+
+
+    def bypass_rec_mode(self):
+        """Bypass the rec mode firmware logic to boot USB."""
+        self.servo.switch_usbkey('host')
+        time.sleep(self.faft_config.usb_plug)
+        self.servo.switch_usbkey('dut')
+
+
+    def trigger_dev_to_rec(self):
+        """Trigger to the rec mode from the dev screen."""
+        # Jetstream does not have this triggering logic.
+        raise NotImplementedError
+
+
+    def trigger_rec_to_dev(self):
+        """Trigger to the dev mode from the rec screen."""
+        self.servo.disable_development_mode()
+        time.sleep(self.faft_config.firmware_screen)
+        self.servo.toggle_development_switch()
+
+
+    def trigger_dev_to_normal(self):
+        """Trigger to the normal mode from the dev screen."""
+        # Jetstream does not have this triggering logic.
+        raise NotImplementedError
+
+
 def _create_fw_bypasser(servo, faft_config):
     """Creates a proper firmware bypasser.
 
@@ -113,6 +159,9 @@
     if bypasser_type == 'ctrl_d_bypasser':
         logging.info('Create a CtrlDBypasser')
         return _CtrlDBypasser(servo, faft_config)
+    if bypasser_type == 'jetstream_bypasser':
+        logging.info('Create a JetstreamBypasser')
+        return _JetstreamBypasser(servo, faft_config)
     else:
         raise NotImplementedError('Not supported fw_bypasser_type: %s',
                                   bypasser_type)
@@ -357,6 +406,26 @@
         self.bypasser.trigger_dev_to_normal()
 
 
+class _JetstreamSwitcher(_BaseModeSwitcher):
+    """Class that switches firmware mode in Jetstream devices."""
+
+    def _enable_dev_mode_and_reboot(self):
+        """Switch to developer mode and reboot."""
+        logging.info("Enabling Jetstream developer mode")
+        self._enable_rec_mode_and_reboot(usb_state='host')
+        self.faft_framework.wait_for_client_offline()
+        self.bypasser.trigger_rec_to_dev()
+
+
+    def _enable_normal_mode_and_reboot(self):
+        """Switch to normal mode and reboot."""
+        logging.info("Disabling Jetstream developer mode")
+        self.servo.disable_development_mode()
+        self._enable_rec_mode_and_reboot(usb_state='host')
+        time.sleep(self.faft_config.firmware_screen)
+        self._disable_rec_mode_and_reboot(usb_state='host')
+
+
 def create_mode_switcher(faft_framework):
     """Creates a proper mode switcher.
 
@@ -369,6 +438,9 @@
     elif switcher_type == 'keyboard_dev_switcher':
         logging.info('Create a KeyboardDevSwitcher')
         return _KeyboardDevSwitcher(faft_framework)
+    elif switcher_type == 'jetstream_switcher':
+        logging.info('Create a JetstreamSwitcher')
+        return _JetstreamSwitcher(faft_framework)
     else:
         raise NotImplementedError('Not supported mode_switcher_type: %s',
                                   switcher_type)
diff --git a/server/cros/servo/servo.py b/server/cros/servo/servo.py
index e0dd832..1b3d87f 100644
--- a/server/cros/servo/servo.py
+++ b/server/cros/servo/servo.py
@@ -146,6 +146,9 @@
     # Time to toggle recovery switch on and off.
     REC_TOGGLE_DELAY = 0.1
 
+    # Time to toggle development switch on and off.
+    DEV_TOGGLE_DELAY = 0.1
+
     # Time between an usb disk plugged-in and detected in the system.
     USB_DETECTION_DELAY = 10
     # Time to keep USB power off before and after USB mux direction is changed
@@ -362,6 +365,13 @@
         self.set('rec_mode', 'off')
 
 
+    def toggle_development_switch(self):
+        """Toggle development switch on and off."""
+        self.enable_development_mode()
+        time.sleep(self.DEV_TOGGLE_DELAY)
+        self.disable_development_mode()
+
+
     def enable_development_mode(self):
         """Enable development mode on device."""
         self.set('dev_mode', 'on')