autotest: servo: Re-work keypress for servo V2

CL gets keypress automation working for servo V2.  See deps commit msg
for more details.

Additionally,
1. Adds the method for ctrl_enter keypress.  See crosbug.com/p/6759
for details.
2. Sets kbd row & columns to none state each time prior to enabling to
guarantee more uniform key triggering.
3. Remove hard-coded config default, 'servo.xml' as servod daemon can
determine which to load via the USB iSerial programmed on the servo
board
4. bug fix for invoking servod via popen.
5. General cleanup of pylint warnings

CQ-DEPEND=I88706ed3dc6f7787fe909c51ef222c969657494a
BUG=none
TEST=manual, wrote small 'throw away' test to invoke keypresses and
inspect them working on both servo V1 & servo V2 interactively

Change-Id: I15fbee5f294c5df3896a07c9d9be8169a36aea32
Reviewed-on: https://gerrit.chromium.org/gerrit/18747
Commit-Ready: Todd Broch <tbroch@chromium.org>
Reviewed-by: Todd Broch <tbroch@chromium.org>
Tested-by: Todd Broch <tbroch@chromium.org>
diff --git a/server/cros/servo.py b/server/cros/servo.py
index c6e6587..404d417 100644
--- a/server/cros/servo.py
+++ b/server/cros/servo.py
@@ -5,7 +5,7 @@
 # Expects to be run in an environment with sudo and no interactive password
 # prompt, such as within the Chromium OS development chroot.
 
-import logging, os, subprocess, time, xmlrpclib
+import logging, os, select, subprocess, sys, time, xmlrpclib
 from autotest_lib.client.bin import utils as client_utils
 from autotest_lib.server import utils
 
@@ -44,6 +44,12 @@
     # Time between an usb disk plugged-in and detected in the system.
     USB_DETECTION_DELAY = 10
 
+    KEY_MATRIX = {
+        'm1': {'ctrl_r': ['0', '0'], 'd': ['0', '1'],
+               'enter': ['1', '0'], 'none': ['1', '1']},
+        'm2': {'ctrl': ['0', '0'], 'refresh': ['0', '1'],
+               'unused': ['1', '0'], 'none': ['1', '1']}
+        }
 
     @staticmethod
     def create_simple(device_under_test_hostname):
@@ -63,7 +69,7 @@
 
 
     def __init__(self, servo_host=None, servo_port=9999,
-                 xml_config=['servo.xml'], servo_vid=None, servo_pid=None,
+                 xml_config=[], servo_vid=None, servo_pid=None,
                  servo_serial=None, cold_reset=False, servo_interfaces=[]):
         """Sets up the servo communication infrastructure.
 
@@ -138,7 +144,7 @@
             value = self.get('pwr_button')
             if value == 'release' or retry > Servo.RELEASE_RETRY_MAX:
                 break
-            logging.info('Waiting for pwr_button to release, retry %d.' % retry)
+            logging.info('Waiting for pwr_button to release, retry %d.', retry)
             retry += 1
             time.sleep(Servo.SHORT_DELAY)
 
@@ -157,14 +163,24 @@
         time.sleep(Servo.SLEEP_DELAY)
 
 
-    def _press_and_release_keys(self, m1, m2, press_secs=None):
+    def _press_and_release_keys(self, m1, m2,
+                                press_secs=SERVO_SEND_SIGNAL_DELAY):
         """Simulate button presses."""
-        if press_secs is None:
-            press_secs = Servo.SERVO_SEND_SIGNAL_DELAY
+        # set keys to none
+        (m2_a1, m2_a0) = self.KEY_MATRIX['m2']['none']
+        (m1_a1, m1_a0) = self.KEY_MATRIX['m1']['none']
+        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)
 
+        (m2_a1, m2_a0) = self.KEY_MATRIX['m2'][m2]
+        (m1_a1, m1_a0) = self.KEY_MATRIX['m1'][m1]
         self.set_nocheck('kbd_en', 'on')
-        self.set_nocheck('kbd_m1', m1)
-        self.set_nocheck('kbd_m2', m2)
+        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)
         time.sleep(press_secs)
         self.set_nocheck('kbd_en', 'off')
 
@@ -174,6 +190,11 @@
         self._press_and_release_keys('d', 'ctrl')
 
 
+    def ctrl_enter(self):
+        """Simulate Ctrl-enter simultaneous button presses."""
+        self._press_and_release_keys('enter', 'ctrl')
+
+
     def d_key(self):
         """Simulate Enter key button press."""
         self._press_and_release_keys('d', 'none')
@@ -234,10 +255,10 @@
         """
         self.set('dut_hub_pwren', 'on')
         if host:
-          self.set('usb_mux_oe1', 'on')
-          self.set('usb_mux_sel1', 'servo_sees_usbkey')
+            self.set('usb_mux_oe1', 'on')
+            self.set('usb_mux_sel1', 'servo_sees_usbkey')
         else:
-          self.set('dut_hub_sel', 'dut_sees_hub')
+            self.set('dut_hub_sel', 'dut_sees_hub')
 
         self.set('dut_hub_on', 'yes')
 
@@ -306,7 +327,7 @@
     def set_nocheck(self, gpio_name, gpio_value):
         """Set the value of a gpio using Servod."""
         assert gpio_name and gpio_value
-        logging.info('Setting %s to %s' % (gpio_name, gpio_value))
+        logging.info('Setting %s to %s', gpio_name, gpio_value)
         self._server.set(gpio_name, gpio_value)
 
 
@@ -385,8 +406,8 @@
                 logging.info('Detecting USB stick device...')
                 usb_dev = self.probe_host_usb_dev()
                 if not usb_dev:
-                  raise Exception('USB device not found')
-                logging.info('Found %s' % usb_dev)
+                    raise Exception('USB device not found')
+                logging.info('Found %s', usb_dev)
             logging.info('Installing image onto usb stick. '
                          'This takes a while...')
             client_utils.poll_for_condition(
@@ -477,21 +498,37 @@
             cmdlist.append('--serialname=%s' % str(servo_serial))
         if servo_interfaces:
             cmdlist.append('--interfaces=%s' % ' '.join(servo_interfaces))
-        logging.info('starting servod w/ cmd :: %s' % ' '.join(cmdlist))
+        logging.info('starting servod w/ cmd :: %s', ' '.join(cmdlist))
         self._servod = subprocess.Popen(cmdlist, 0, None, None, None,
                                         subprocess.PIPE)
         # wait for servod to initialize
         timeout = Servo.MAX_SERVO_STARTUP_DELAY
-        while ('Listening' not in self._servod.stderr.readline() and
-               self._servod.returncode is None and timeout > 0):
-            time.sleep(1)
-            timeout -= 1
-        assert self._servod.returncode is None and timeout
+        start_time = time.time()
+        listening = False
+        while (time.time() - start_time) < timeout and \
+                self._servod.returncode is None:
+            (rfds, _, _) = select.select([self._servod.stderr], [], [], 0)
+            if len(rfds) > 0:
+                if 'Listening' in rfds[0].readline():
+                    listening = True
+                    break
+
+        if not listening:
+            logging.fatal("Unable to successfully launch servod")
+            sys.exit(-1)
 
 
     def _init_seq(self):
         """Initiate starting state for servo."""
-        self.set('tx_dir', 'input')
+        # TODO(tbroch) This is only a servo V1 control.  Need to add ability in
+        # servod to easily identify version so I can make this conditional not
+        # try and fail quietly
+        try:
+            self.set('tx_dir', 'input')
+        except:
+            logging.warning("Failed to set tx_dir.  This is ok if not servo V1")
+
+
         # TODO(tbroch) Investigate method to determine DUT's type so we can
         # conditionally set lid if applicable
         self.set_nocheck('lid_open', 'yes')