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/udev_interface.h b/udev_interface.h
new file mode 100644
index 0000000..b9765a8
--- /dev/null
+++ b/udev_interface.h
@@ -0,0 +1,193 @@
+// Copyright (c) 2012 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UDEV_INTERFACE_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UDEV_INTERFACE_H__
+
+#include <libudev.h>
+
+#include "update_engine/utils.h"
+
+// An interface for libudev calls, allowing to easily mock it.
+
+namespace chromeos_update_engine {
+
+// An interface for libudev methods that are being used in update engine.
+//
+// TODO(garnold) As is, this is a pretty lame indirection layer that otherwise
+// does not provide any better abstraction than the existing libudev API. Done
+// properly, we should replace it with encapsulated udev, enumerate and device
+// objects, and hide initialization, reference handling and iterators in ways
+// more appropriate to an object-oriented setting...
+class UdevInterface {
+ public:
+ // Interfaces for various udev closers. All of these are merely containers for
+ // a single pointer to some udev handle, which invoke the provided interface's
+ // unref method and nullify the handle upon destruction. It should suffice for
+ // derivative (concrete) interfaces to implement the various unref methods to
+ // fit their needs, making these closers behave as expected.
+ class UdevCloser {
+ public:
+ explicit UdevCloser(UdevInterface* udev_iface, struct udev** udev_p)
+ : udev_iface_(udev_iface), udev_p_(udev_p) {
+ CHECK(udev_iface && udev_p);
+ }
+ virtual ~UdevCloser() {
+ if (*udev_p_) {
+ udev_iface_->Unref(*udev_p_);
+ *udev_p_ = NULL;
+ }
+ }
+ protected:
+ UdevInterface* udev_iface_;
+ struct udev** udev_p_;
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(UdevCloser);
+ };
+
+ class UdevEnumerateCloser {
+ public:
+ explicit UdevEnumerateCloser(UdevInterface* udev_iface,
+ struct udev_enumerate** udev_enum_p)
+ : udev_iface_(udev_iface), udev_enum_p_(udev_enum_p) {
+ CHECK(udev_iface && udev_enum_p);
+ }
+ virtual ~UdevEnumerateCloser() {
+ if (*udev_enum_p_) {
+ udev_iface_->EnumerateUnref(*udev_enum_p_);
+ *udev_enum_p_ = NULL;
+ }
+ }
+ protected:
+ UdevInterface* udev_iface_;
+ struct udev_enumerate** udev_enum_p_;
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(UdevEnumerateCloser);
+ };
+
+ class UdevDeviceCloser {
+ public:
+ explicit UdevDeviceCloser(UdevInterface* udev_iface,
+ struct udev_device** udev_dev_p)
+ : udev_iface_(udev_iface), udev_dev_p_(udev_dev_p) {
+ CHECK(udev_iface && udev_dev_p);
+ }
+ virtual ~UdevDeviceCloser() {
+ if (*udev_dev_p_) {
+ udev_iface_->DeviceUnref(*udev_dev_p_);
+ *udev_dev_p_ = NULL;
+ }
+ }
+ protected:
+ UdevInterface* udev_iface_;
+ struct udev_device** udev_dev_p_;
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(UdevDeviceCloser);
+ };
+
+ virtual UdevCloser* NewUdevCloser(struct udev** udev_p) {
+ return new UdevCloser(this, udev_p);
+ }
+ virtual UdevEnumerateCloser* NewUdevEnumerateCloser(
+ struct udev_enumerate** udev_enum_p) {
+ return new UdevEnumerateCloser(this, udev_enum_p);
+ }
+ virtual UdevDeviceCloser* NewUdevDeviceCloser(
+ struct udev_device** udev_dev_p) {
+ return new UdevDeviceCloser(this, udev_dev_p);
+ }
+
+ // Lists.
+ virtual const char* ListEntryGetName(struct udev_list_entry* list_entry) = 0;
+ virtual udev_list_entry* ListEntryGetNext(
+ struct udev_list_entry* list_entry) = 0;
+
+ // Device methods.
+ virtual struct udev_device* DeviceNewFromSyspath(
+ struct udev* udev,
+ const char* syspath) = 0;
+ virtual const char* DeviceGetPropertyValue(struct udev_device* udev_device,
+ const char* key) = 0;
+ virtual const char* DeviceGetSyspath(
+ struct udev_device* udev_device) = 0;
+ virtual void DeviceUnref(struct udev_device* udev_device) = 0;
+
+ // Enumerate methods.
+ virtual struct udev_enumerate* EnumerateNew(struct udev* udev) = 0;
+ virtual int EnumerateAddMatchSubsystem(struct udev_enumerate* udev_enum,
+ const char* subsystem) = 0;
+ virtual int EnumerateAddMatchSysname(struct udev_enumerate* udev_enum,
+ const char* sysname) = 0;
+ virtual int EnumerateScanDevices(struct udev_enumerate* udev_enum) = 0;
+ virtual struct udev_list_entry* EnumerateGetListEntry(
+ struct udev_enumerate* udev_enum) = 0;
+ virtual void EnumerateUnref(struct udev_enumerate* udev_enum) = 0;
+
+ // Udev instance methods.
+ virtual struct udev* New() = 0;
+ virtual void Unref(struct udev* udev) = 0;
+};
+
+
+// Implementation of libudev interface using concrete udev calls.
+class StandardUdevInterface : public UdevInterface {
+ public:
+ // Concrete udev API wrappers utilizing the standard libudev calls.
+ virtual const char* ListEntryGetName(struct udev_list_entry* list_entry) {
+ return udev_list_entry_get_name(list_entry);
+ }
+ virtual struct udev_list_entry* ListEntryGetNext(
+ struct udev_list_entry* list_entry) {
+ return udev_list_entry_get_next(list_entry);
+ }
+
+ virtual struct udev_device* DeviceNewFromSyspath(struct udev* udev,
+ const char* syspath) {
+ return udev_device_new_from_syspath(udev, syspath);
+ }
+ virtual const char* DeviceGetPropertyValue(struct udev_device* udev_device,
+ const char* key) {
+ return udev_device_get_property_value(udev_device, key);
+ }
+ virtual const char* DeviceGetSyspath(struct udev_device* udev_device) {
+ return udev_device_get_syspath(udev_device);
+ }
+ virtual void DeviceUnref(struct udev_device* udev_device) {
+ return udev_device_unref(udev_device);
+ }
+
+ virtual struct udev_enumerate* EnumerateNew(struct udev* udev) {
+ return udev_enumerate_new(udev);
+ }
+ virtual int EnumerateAddMatchSubsystem(struct udev_enumerate* udev_enum,
+ const char* subsystem) {
+ return udev_enumerate_add_match_subsystem(udev_enum, subsystem);
+ }
+ virtual int EnumerateAddMatchSysname(struct udev_enumerate* udev_enum,
+ const char* sysname) {
+ return udev_enumerate_add_match_sysname(udev_enum, sysname);
+ }
+ virtual int EnumerateScanDevices(struct udev_enumerate* udev_enum) {
+ return udev_enumerate_scan_devices(udev_enum);
+ }
+ virtual struct udev_list_entry* EnumerateGetListEntry(
+ struct udev_enumerate* udev_enum) {
+ return udev_enumerate_get_list_entry(udev_enum);
+ }
+ virtual void EnumerateUnref(struct udev_enumerate* udev_enum) {
+ return udev_enumerate_unref(udev_enum);
+ }
+
+ virtual struct udev* New() {
+ return udev_new();
+ }
+ virtual void Unref(struct udev* udev) {
+ return udev_unref(udev);
+ }
+};
+
+} // namespace chromeos_update_engine
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UDEV_INTERFACE_H__
+