Revised GPIO module interface + GPIO discovery logic

* The GpioHandler class is no longer a static singleton, rather an
  ordinary object with a dynamic guard against multiple instances. This
  makes testing/mocking a lot easier and simplifies implementation.

* It uses a basic, mockable udev interface; the module comes with
  complete unit testing of the discovery mechanism.

* Corresponding changes to user classes, including UpdateAttempter and
  UpdateCheckScheduler.

Note that the implementation of the test mode signaling protocol is
currently a no-op, always returning false, and hence has no effect on
the update process yet. This mechanism will be implemented in a later
CL.

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

Change-Id: I2f6254db6799ff5ef8616314890833f6e3269ff6
Reviewed-on: https://gerrit.chromium.org/gerrit/22869
Reviewed-by: Gilad Arnold <garnold@chromium.org>
Tested-by: Gilad Arnold <garnold@chromium.org>
Commit-Ready: Gilad Arnold <garnold@chromium.org>
diff --git a/gpio_handler.h b/gpio_handler.h
index c3d5a38..b269266 100644
--- a/gpio_handler.h
+++ b/gpio_handler.h
@@ -9,61 +9,192 @@
 
 #include <string>
 
-#include <base/basictypes.h>
+#include <base/time.h>
+
+#include "update_engine/file_descriptor.h"
+#include "update_engine/udev_interface.h"
 
 namespace chromeos_update_engine {
 
-// Handles GPIO signals, which are used for indicating a lab test environment.
-// This class detects, reads and decides whether a test scenario is in effect,
-// which can be used by client code to trigger test-specific behavior.  This
-// class has only static members/methods and cannot be instantiated.
+// An abstract GPIO handler interface. Serves as basic for both concrete and
+// mock implementations.
 class GpioHandler {
  public:
-  // Returns true iff GPIOs have been signaled to indicate an automated test
-  // case. This triggers a discovery and reading of the dutflaga/b GPIOs.
-  static bool IsGpioSignalingTest();
+  GpioHandler() {}
+  virtual ~GpioHandler() {};  // ensure virtual destruction
+
+  // Returns true iff GPIOs have been used to signal an automated test case.
+  // This call may trigger a (deferred) GPIO discovery step prior to engaging in
+  // the signaling protocol; if discovery did not reveal GPIO devices, or the
+  // protocol has terminated prematurely, it will conservatively default to
+  // false.
+  virtual bool IsTestModeSignaled() = 0;
 
  private:
-  // This class cannot be instantiated.
-  GpioHandler() {}
+  DISALLOW_COPY_AND_ASSIGN(GpioHandler);
+};
 
-  // Enumerator for dutflag GPIOs.
-  enum DutflagGpioId {
-    kDutflagaGpio,
-    kDutflagbGpio,
+
+// Concrete implementation of GPIO signal handling. Currently, it only utilizes
+// the two Chromebook-specific GPIOs (aka 'dut_flaga' and 'dut_flagb') in
+// deciding whether a lab test mode has been signaled.  Internal logic includes
+// detection, setup and reading from / writing to GPIOs. Detection is done via
+// libudev calls.  This class should be instantiated at most once to avoid race
+// conditions in communicating over GPIO signals; instantiating a second
+// 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);
+
+  // Free all resources, allow to reinstantiate.
+  virtual ~StandardGpioHandler();
+
+  // Returns true iff GPIOs have been used to signal an automated test case. The
+  // check is performed at most once and the result is cached and returned on
+  // subsequent calls, unless |is_force| is true. This call may trigger a
+  // delayed GPIO discovery prior to engaging in the signaling protocol; if the
+  // delay period has not elapsed, it will conservatively default to false.
+  virtual bool IsTestModeSignaled();
+
+ private:
+  // GPIO identifiers, currently includes only the two dutflags.
+  enum GpioId {
+    kGpioIdDutflaga = 0,
+    kGpioIdDutflagb,
+    kGpioIdMax  // marker, do not remove!
   };
 
-  // Gets the fully qualified sysfs name of a dutflag device.  |udev| is a live
-  // libudev instance; |gpio_dutflag_str| is the identifier for the requested
-  // dutflag GPIO. The output is stored in the string pointed to by
-  // |dutflag_dev_name_p|.  Returns true upon success, false otherwise.
-  static bool GetDutflagGpioDevName(struct udev* udev,
-                                    const std::string& gpio_dutflag_str,
-                                    const char** dutflag_dev_name_p);
+  // GPIO direction specifier.
+  enum GpioDir {
+    kGpioDirIn = 0,
+    kGpioDirOut,
+    kGpioDirMax  // marker, do not remove!
+  };
 
-  // Gets the dut_flaga/b GPIO device names and copies them into the two string
-  // arguments, respectively. A string pointer may be null, in which case
-  // discovery will not be attempted for the corresponding device. The function
-  // caches these strings, which are assumed to be hardware constants. Returns
+  // GPIO value.
+  enum GpioVal {
+    kGpioValUp = 0,
+    kGpioValDown,
+    kGpioValMax  // marker, do not remove!
+  };
+
+  // GPIO definition data.
+  struct GpioDef {
+    const char* name;           // referential name of this GPIO
+    const char* udev_property;  // udev property containing the device id
+  };
+
+  // GPIO runtime control structure.
+  struct Gpio {
+    std::string descriptor;  // unique GPIO descriptor
+    std::string dev_path;    // sysfs device name
+  };
+
+  // Various constants.
+  static const int kServoInputResponseTimeoutInSecs = 3;
+  static const int kServoInputNumChecksPerSec = 5;
+  static const int kServoOutputResponseWaitInSecs = 2;
+
+  // GPIO value/direction conversion tables.
+  static const char* gpio_dirs_[kGpioDirMax];
+  static const char* gpio_vals_[kGpioValMax];
+
+  // GPIO definitions.
+  static const GpioDef gpio_defs_[kGpioIdMax];
+
+  // Udev device enumeration helper classes. First here is an interface
+  // definition, which provides callbacks for enumeration setup and processing.
+  class UdevEnumHelper {
+   public:
+    UdevEnumHelper(StandardGpioHandler* gpio_handler)
+        : gpio_handler_(gpio_handler) {}
+
+    // Setup the enumeration filters.
+    virtual bool SetupEnumFilters(udev_enumerate* udev_enum) = 0;
+
+    // Processes an enumerated device. Returns true upon success, false
+    // otherwise.
+    virtual bool ProcessDev(udev_device* dev) = 0;
+
+    // Finalize the enumeration.
+    virtual bool Finalize() = 0;
+
+   protected:
+    StandardGpioHandler* gpio_handler_;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(UdevEnumHelper);
+  };
+
+  // Specialized udev enumerate helper for extracting GPIO descriptors from the
+  // GPIO chip device.
+  class GpioChipUdevEnumHelper : public UdevEnumHelper {
+   public:
+    GpioChipUdevEnumHelper(StandardGpioHandler* gpio_handler)
+        : UdevEnumHelper(gpio_handler), num_gpio_chips_(0) {}
+    virtual bool SetupEnumFilters(udev_enumerate* udev_enum);
+    virtual bool ProcessDev(udev_device* dev);
+    virtual bool Finalize();
+
+   private:
+    // Records the number of times a GPIO chip has been enumerated (should not
+    // exceed 1).
+    int num_gpio_chips_;
+
+    DISALLOW_COPY_AND_ASSIGN(GpioChipUdevEnumHelper);
+  };
+
+  // Specialized udev enumerate helper for extracting a sysfs device path from a
+  // GPIO device.
+  class GpioUdevEnumHelper : public UdevEnumHelper {
+   public:
+    GpioUdevEnumHelper(StandardGpioHandler* gpio_handler, GpioId id)
+        : UdevEnumHelper(gpio_handler), num_gpios_(0), id_(id) {}
+    virtual bool SetupEnumFilters(udev_enumerate* udev_enum);
+    virtual bool ProcessDev(udev_device* dev);
+    virtual bool Finalize();
+
+   private:
+    // Records the number of times a GPIO has been enumerated with a given
+    // descriptor (should not exceed 1).
+    int num_gpios_;
+
+    // The enumerated GPIO identifier.
+    GpioId id_;
+
+    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();
+
+  // An initialization helper performing udev enumeration. |enum_helper|
+  // implements an enumeration initialization and processing methods. Returns
   // true upon success, false otherwise.
-  static bool GetDutflagGpioDevNames(std::string* dutflaga_dev_name_p,
-                                     std::string* dutflagb_dev_name_p);
+  bool InitUdevEnum(struct udev* udev, UdevEnumHelper* enum_helper);
 
-  // Writes the dut_flaga GPIO status into its argument, where true/false stand
-  // for "on"/"off", respectively. Returns true upon success, false otherwise
-  // (in which case no value is written to |status|).
-  static bool GetDutflagaGpio(bool* status);
+  // 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);
 
-  // Reads the value of a dut_flag GPIO |id| and stores it in |status_p|.
-  // Returns true upon success, false otherwise (which also means that the GPIO
-  // value was not stored and should not be used).
-  static bool GetDutflagGpioStatus(DutflagGpioId id, bool* status_p);
+  // 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_;
 
-  // Dutflaga/b GPIO device names.
-  static const char* dutflaga_dev_name_;
-  static const char* dutflagb_dev_name_;
+  // GPIO control structures.
+  Gpio gpios_[kGpioIdMax];
 
-  DISALLOW_COPY_AND_ASSIGN(GpioHandler);
+  // Udev interface.
+  UdevInterface* const udev_iface_;
+
+  // Indicates whether GPIO discovery was performed.
+  bool is_discovery_attempted_;
+
+  DISALLOW_COPY_AND_ASSIGN(StandardGpioHandler);
 };
 
 }  // namespace chromeos_update_engine