servo: abstract dts mode logic

This change adds a few helpers to handle dts mode

- dts_mode_is_valid: whether dts mode can be supported.
- dts_mode_is_safe: whether controlling dts mode will remove the
main device's console access

- get_dts_mode: helper to get the dts mode

BUG=chromium:1009616

TEST=test_that --autotest_dir . $DIP firmware_Cr50CCDServoCap

/tmp/test_that_results_b2EdTg/results-1-firmware_Cr50CCDServoCap
[  PASSED  ]

Note: all tests below are done by adding two logging statements into
initialize_dut() and subsequently asseting False to speed debug.

TEST=sudo servod -b atlas // v4 (type-c) + micro
     test_that --autotest_dir . $AIP power_Monitoring -b atlas

12:03:08 INFO | autoserv| TYPE: servo_v4_with_servo_micro
12:03:08 INFO | autoserv| DTS MODE: True

TEST=sudo servod -b atlas // v4 (type-c) + ccd
     test_that --autotest_dir . $AIP power_Monitoring -b atlas

12:05:20 INFO | autoserv| TYPE: servo_v4_with_ccd_cr50
12:05:20 INFO | autoserv| DTS MODE: True

TEST=sudo servod -b atlas // v4 (type-A) + micro
     test_that --autotest_dir . $AIP power_Monitoring -b atlas

12:06:49 INFO | autoserv| TYPE: servo_v4_with_servo_micro
12:06:50 INFO | autoserv| DTS controls require a type-c servo v4.
12:06:50 INFO | autoserv| DTS MODE: False

TEST=sudo servod -b atlas // v2
     test_that --autotest_dir . $AIP power_Monitoring -b atlas

12:08:19 INFO | autoserv| TYPE: servo_v2
12:08:19 INFO | autoserv| DTS MODE: False

Change-Id: I7f342691bf84896df5a779555bf33334ff1c17b7
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/1835913
Tested-by: Ruben Rodriguez Buchillon <coconutruben@chromium.org>
Commit-Queue: Ruben Rodriguez Buchillon <coconutruben@chromium.org>
Reviewed-by: Mary Ruthven <mruthven@chromium.org>
diff --git a/server/cros/servo/servo.py b/server/cros/servo/servo.py
index 2908a78..54b5cba 100644
--- a/server/cros/servo/servo.py
+++ b/server/cros/servo/servo.py
@@ -1078,6 +1078,25 @@
         """Whether the main servo device (no prefixes) is a legacy device."""
         return not self.main_device_is_ccd()
 
+
+    def main_device_is_active(self):
+        """Return whether the main device is the active device.
+
+        This is only relevant for a dual setup with ccd and legacy on the same
+        DUT. The main device is the servo that has no prefix on its controls.
+        This helper answers the question whether that device is also the
+        active device or not.
+        """
+        # TODO(coconutruben): The current implementation of the dual setup only
+        # ever has legacy as the main device. Therefore, it suffices to ask
+        # whether the active device is ccd.
+        if not self.dts_mode_is_valid():
+            # Use dts support as a proxy to whether the servo setup could
+            # support a dual role. Only those setups now support legacy and ccd.
+            return True
+        active_device = self.get('active_v4_device')
+        return 'ccd_cr50' not in active_device
+
     def _initialize_programmer(self, rw_only=False):
         """Initialize the firmware programmer.
 
@@ -1321,8 +1340,40 @@
         logging.info('Charger port voltage: %dmV', chg_port_mv)
         return True
 
-    def set_servo_v4_dts_mode(self, state):
-        """Set servo v4 dts mode to off or on.
+    def dts_mode_is_valid(self):
+        """Return whether servo setup supports dts mode control for cr50."""
+        if 'servo_v4' not in self._servo_type:
+            # Only servo v4 supports this feature.
+            logging.debug('%r type does not support dts mode control.',
+                          self._servo_type)
+            return False
+        # On servo v4, it still needs ot be the type-c version.
+        if not 'type-c' == self.get('servo_v4_type'):
+            logging.info('DTS controls require a type-c servo v4.')
+            return False
+        return True
+
+    def dts_mode_is_safe(self):
+        """Return whether servo setup supports dts mode without losing access.
+
+        DTS mode control exists but the main device might go through ccd.
+        In that case, it's only safe to control dts mode if the main device
+        is legacy as otherwise the connection to the main device cuts out.
+        """
+        return self.dts_mode_is_valid() and self.main_device_is_flex()
+
+    def get_dts_mode(self):
+        """Return servo dts mode.
+
+        @returns: on/off whether dts is on or off
+        """
+        if not self.dts_mode_is_valid():
+            logging.info('Not a valid servo setup. Unable to get dts mode.')
+            return
+        return self.get('servo_v4_dts_mode')
+
+    def set_dts_mode(self, state):
+        """Set servo dts mode to off or on.
 
         It does nothing if not a servo v4. Disable the ccd watchdog if we're
         disabling dts mode. CCD will disconnect. The watchdog only allows CCD
@@ -1331,8 +1382,9 @@
 
         @param state: Set servo v4 dts mode 'off' or 'on'.
         """
-        if not self._servo_type.startswith('servo_v4'):
-            logging.debug('Not a servo v4, unable to set dts mode %s.', state)
+        if not self.dts_mode_is_valid():
+            logging.info('Not a valid servo setup. Unable to set dts mode %s.',
+                         state)
             return
 
         # TODO(mruthven): remove watchdog check once the labstation has been