Bidirectional test mode signaling over GPIO

* Implements a bidirectional synchronous handshake protocol over
  dut_flaga/b GPIOs. This protocol is designed to return true (test
  mode) only if the DUT is connected to a servo board which implements
  the remote end.

* Includes unit tests for the test mode signaling routine, complete with
  mock/fake implementation of the remote end.

Note that we still do not deploy GpioHandler in actual update
processing, which will be done later.

BUG=chromium-os:25397,chromium-os:27109,chromium-os:27672
TEST=Builds and passes unit tests (including new ones)

Change-Id: I265407ed735c3e1354e10782ac30566b16caeb20
Reviewed-on: https://gerrit.chromium.org/gerrit/23330
Reviewed-by: Gaurav Shah <gauravsh@chromium.org>
Tested-by: Gilad Arnold <garnold@chromium.org>
Commit-Ready: Gilad Arnold <garnold@chromium.org>
Reviewed-by: Gilad Arnold <garnold@chromium.org>
diff --git a/gpio_handler.h b/gpio_handler.h
index b269266..0868cb5 100644
--- a/gpio_handler.h
+++ b/gpio_handler.h
@@ -44,10 +44,14 @@
 // object will actually cause a runtime error.
 class StandardGpioHandler : public GpioHandler {
  public:
-  // This constructor accepts a udev interface |udev_iface|. The value of
-  // |is_defer_discovery| determines whether GPIO discovery should be attempted
-  // right away (false) or done lazily, when necessitated by other calls (true).
-  StandardGpioHandler(UdevInterface* udev_iface, bool is_defer_discovery);
+  // This constructor accepts a udev interface |udev_iface| and a reusable file
+  // descriptor |fd|. The value of |is_defer_discovery| determines whether GPIO
+  // discovery should be attempted right away (false) or done lazily, when
+  // necessitated by other calls (true). If |is_cache_test_mode| is true,
+  // checking for test mode signal is done only once and further queries return
+  // the cached result.
+  StandardGpioHandler(UdevInterface* udev_iface, FileDescriptor* fd,
+                      bool is_defer_discovery, bool is_cache_test_mode);
 
   // Free all resources, allow to reinstantiate.
   virtual ~StandardGpioHandler();
@@ -93,11 +97,21 @@
     std::string dev_path;    // sysfs device name
   };
 
-  // Various constants.
-  static const int kServoInputResponseTimeoutInSecs = 3;
-  static const int kServoInputNumChecksPerSec = 5;
+  // The number of seconds we wait before flipping the output signal (aka,
+  // producing the "challenge" signal). Assuming a 1 second sampling frequency
+  // on the servo side, a two second wait should be enough.
   static const int kServoOutputResponseWaitInSecs = 2;
 
+  // The total number of seconds we wait for a servo response from the point we
+  // flip the output signal. Assuming a 1 second sampling frequency on the servo
+  // side, a two second wait should suffice. We add one more second for grace
+  // (servod / hardware processing delays, etc).
+  static const int kServoInputResponseTimeoutInSecs = 3;
+
+  // The number of times per second we check for a servo response. Five seems
+  // like a reasonable value.
+  static const int kServoInputNumChecksPerSec = 5;
+
   // GPIO value/direction conversion tables.
   static const char* gpio_dirs_[kGpioDirMax];
   static const char* gpio_vals_[kGpioValMax];
@@ -168,19 +182,99 @@
     DISALLOW_COPY_AND_ASSIGN(GpioUdevEnumHelper);
   };
 
-  // Attempt GPIO discovery, at most once. Returns true if discovery process was
-  // successfully completed or already attempted, false otherwise.
-  bool DiscoverGpios();
+  // Helper class for resetting a GPIO direction.
+  class GpioDirResetter {
+   public:
+    GpioDirResetter(StandardGpioHandler* handler, GpioId id, GpioDir dir);
+    ~GpioDirResetter();
+
+    bool do_reset() const {
+      return do_reset_;
+    }
+    bool set_do_reset(bool do_reset) {
+      return (do_reset_ = do_reset);
+    }
+
+   private:
+    // Determines whether or not the GPIO direction should be reset to the
+    // initial value.
+    bool do_reset_;
+
+    // The GPIO handler to use for changing the GPIO direction.
+    StandardGpioHandler* handler_;
+
+    // The GPIO identifier and initial direction.
+    GpioId id_;
+    GpioDir dir_;
+  };
 
   // An initialization helper performing udev enumeration. |enum_helper|
   // implements an enumeration initialization and processing methods. Returns
   // true upon success, false otherwise.
   bool InitUdevEnum(struct udev* udev, UdevEnumHelper* enum_helper);
 
+  // Resets the object's flags which determine the status of test mode
+  // signaling.
+  void ResetTestModeSignalingFlags();
+
+  // Attempt GPIO discovery, at most once. Returns true if discovery process was
+  // successfully completed or already attempted, false otherwise.
+  bool DiscoverGpios();
+
   // Assigns a copy of the device name of GPIO |id| to |dev_path_p|. Assumes
   // initialization. Returns true upon success, false otherwise.
   bool GetGpioDevName(GpioId id, std::string* dev_path_p);
 
+  // Open a sysfs file device |dev_name| of GPIO |id|, for either reading or
+  // writing depending on |is_write|.  Uses the internal file descriptor for
+  // this purpose, which can be reused as long as it is closed between
+  // successive opens. Returns true upon success, false otherwise (optionally,
+  // with errno set accordingly).
+  bool OpenGpioFd(GpioId id, const char* dev_name, bool is_write);
+
+  // Writes a value to device |dev_name| of GPIO |id|. The index |output| is
+  // used to index the corresponding string to be written from the list
+  // |entries| of length |num_entries|.  Returns true upon success, false
+  // otherwise.
+  bool SetGpio(GpioId id, const char* dev_name, const char* entries[],
+               const int num_entries, int output);
+
+  // Reads a value from device |dev_name| of GPIO |id|. The list |entries| of
+  // length |num_entries| is used to convert the read string into an index,
+  // which is written to |input_p|. The call will fail if the value being read
+  // is not listed in |entries|. Returns true upon success, false otherwise.
+  bool GetGpio(GpioId id, const char* dev_name, const char* entries[],
+               const int num_entries, int* input_p);
+
+  // Sets GPIO |id| to to operate in a given |direction|. Assumes
+  // initialization. Returns true on success, false otherwise.
+  bool SetGpioDirection(GpioId id, GpioDir direction);
+
+  // Assigns the current direction of GPIO |id| into |direction_p|. Assumes
+  // initialization. Returns true on success, false otherwise.
+  bool GetGpioDirection(GpioId id, GpioDir* direction_p);
+
+  // Sets the value of GPIO |id| to |value|. Assumues initialization. The GPIO
+  // direction should be set to 'out' prior to this call. If
+  // |is_check_direction| is true, it'll ensure that the direction is indeed
+  // 'out' prior to attempting the write. Returns true on success, false
+  // otherwise.
+  bool SetGpioValue(GpioId id, GpioVal value, bool is_check_direction);
+
+  // Reads the value of a GPIO |id| and stores it in |value_p|. Assumes
+  // initialization.  The GPIO direction should be set to 'in' prior to this
+  // call. If |is_check_direction| is true, it'll ensure that the direction is
+  // indeed 'in' prior to attempting the read. Returns true upon success, false
+  // otherwise.
+  bool GetGpioValue(GpioId id, GpioVal *value_p, bool is_check_direction);
+
+  // Invokes the actual GPIO handshake protocol to determine whether test mode
+  // was signaled. Returns true iff the handshake has terminated gracefully
+  // without encountering any errors; note that a true value does *not* mean
+  // that a test mode signal has been detected.  The spec for this protocol:
+  // https://docs.google.com/a/google.com/document/d/1DB-35ptck1wT1TYrgS5AC5Y3ALfHok-iPA7kLBw2XCI/edit
+  bool DoTestModeSignalingProtocol();
+
   // Dynamic counter for the number of instances this class has. Used to enforce
   // that no more than one instance created. Thread-unsafe.
   static unsigned num_instances_;
@@ -191,9 +285,21 @@
   // Udev interface.
   UdevInterface* const udev_iface_;
 
+  // A file abstraction for handling GPIO devices.
+  FileDescriptor* const fd_;
+
+  // Determines whether test mode signal should be checked at most once and
+  // cached, or reestablished on each query.
+  const bool is_cache_test_mode_;
+
   // Indicates whether GPIO discovery was performed.
   bool is_discovery_attempted_;
 
+  // Persistent state of the test mode check.
+  bool is_first_check_;
+  bool is_handshake_completed_;
+  bool is_test_mode_;
+
   DISALLOW_COPY_AND_ASSIGN(StandardGpioHandler);
 };