blob: d1825bde482feaadd2a6a389638bcd76fc927ce7 [file] [log] [blame]
// 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.
#include "gpio_handler.h"
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <base/eintr_wrapper.h>
#include <base/string_util.h>
#include <glib.h>
#include "update_engine/utils.h"
using std::string;
namespace chromeos_update_engine {
const char* GpioHandler::dutflaga_dev_name_ = NULL;
const char* GpioHandler::dutflagb_dev_name_ = NULL;
namespace {
// Names of udev properties that are linked to the GPIO chip device and identify
// the two dutflag GPIOs on different boards.
const char kIdGpioDutflaga[] = "ID_GPIO_DUTFLAGA";
const char kIdGpioDutflagb[] = "ID_GPIO_DUTFLAGB";
// Scoped closer for udev and udev_enumerate objects.
// TODO(garnold) chromium-os:26934: it would be nice to generalize the different
// ScopedFooCloser implementations in update engine using a single template.
class ScopedUdevCloser {
public:
explicit ScopedUdevCloser(udev** udev_p) : udev_p_(udev_p) {}
~ScopedUdevCloser() {
if (udev_p_ && *udev_p_) {
udev_unref(*udev_p_);
*udev_p_ = NULL;
}
}
private:
struct udev **udev_p_;
DISALLOW_COPY_AND_ASSIGN(ScopedUdevCloser);
};
class ScopedUdevEnumerateCloser {
public:
explicit ScopedUdevEnumerateCloser(udev_enumerate **udev_enum_p) :
udev_enum_p_(udev_enum_p) {}
~ScopedUdevEnumerateCloser() {
if (udev_enum_p_ && *udev_enum_p_) {
udev_enumerate_unref(*udev_enum_p_);
*udev_enum_p_ = NULL;
}
}
private:
struct udev_enumerate** udev_enum_p_;
DISALLOW_COPY_AND_ASSIGN(ScopedUdevEnumerateCloser);
};
} // namespace {}
bool GpioHandler::IsGpioSignalingTest() {
// Peek dut_flaga GPIO state.
bool dutflaga_gpio_state;
if (!GetDutflagGpioStatus(kDutflagaGpio, &dutflaga_gpio_state)) {
LOG(WARNING) << "dutflaga GPIO reading failed, defaulting to non-test mode";
return false;
}
LOG(INFO) << "dutflaga GPIO reading: "
<< (dutflaga_gpio_state ? "on (non-test mode)" : "off (test mode)");
return !dutflaga_gpio_state;
}
bool GpioHandler::GetDutflagGpioDevName(struct udev* udev,
const string& gpio_dutflag_str,
const char** dutflag_dev_name_p) {
CHECK(udev && dutflag_dev_name_p);
struct udev_enumerate* udev_enum = NULL;
int num_gpio_dutflags = 0;
const string gpio_dutflag_pattern = "*" + gpio_dutflag_str;
int ret;
// Initialize udev enumerate context and closer.
if (!(udev_enum = udev_enumerate_new(udev))) {
LOG(ERROR) << "failed to obtain udev enumerate context";
return false;
}
ScopedUdevEnumerateCloser udev_enum_closer(&udev_enum);
// Populate filters for find an initialized GPIO chip.
if ((ret = udev_enumerate_add_match_subsystem(udev_enum, "gpio")) ||
(ret = udev_enumerate_add_match_sysname(udev_enum,
gpio_dutflag_pattern.c_str()))) {
LOG(ERROR) << "failed to initialize udev enumerate context (" << ret << ")";
return false;
}
// Obtain list of matching devices.
if ((ret = udev_enumerate_scan_devices(udev_enum))) {
LOG(ERROR) << "udev enumerate context scan failed (error code "
<< ret << ")";
return false;
}
// Iterate over matching devices, obtain GPIO dut_flaga identifier.
struct udev_list_entry* list_entry;
udev_list_entry_foreach(list_entry,
udev_enumerate_get_list_entry(udev_enum)) {
// Make sure we're not enumerating more than one device.
num_gpio_dutflags++;
if (num_gpio_dutflags > 1) {
LOG(WARNING) <<
"enumerated multiple dutflag GPIOs, ignoring this one";
continue;
}
// Obtain device name.
const char* dev_name = udev_list_entry_get_name(list_entry);
if (!dev_name) {
LOG(WARNING) << "enumerated device has a null name string, skipping";
continue;
}
// Obtain device object.
struct udev_device* dev = udev_device_new_from_syspath(udev, dev_name);
if (!dev) {
LOG(WARNING) <<
"obtained a null device object for enumerated device, skipping";
continue;
}
// Obtain device syspath.
const char* dev_syspath = udev_device_get_syspath(dev);
if (dev_syspath) {
LOG(INFO) << "obtained device syspath: " << dev_syspath;
*dutflag_dev_name_p = strdup(dev_syspath);
} else {
LOG(WARNING) << "could not obtain device syspath";
}
udev_device_unref(dev);
}
return true;
}
bool GpioHandler::GetDutflagGpioDevNames(string* dutflaga_dev_name_p,
string* dutflagb_dev_name_p) {
if (!(dutflaga_dev_name_p || dutflagb_dev_name_p))
return true; // No output pointers, nothing to do.
string gpio_dutflaga_str, gpio_dutflagb_str;
if (!(dutflaga_dev_name_ && dutflagb_dev_name_)) {
struct udev* udev = NULL;
struct udev_enumerate* udev_enum = NULL;
int num_gpio_chips = 0;
const char* id_gpio_dutflaga = NULL;
const char* id_gpio_dutflagb = NULL;
int ret;
LOG(INFO) << "begin discovery of dut_flaga/b devices";
// Obtain libudev instance and closer.
if (!(udev = udev_new())) {
LOG(ERROR) << "failed to obtain libudev instance";
return false;
}
ScopedUdevCloser udev_closer(&udev);
// Initialize a udev enumerate object and closer with a bounded lifespan.
{
if (!(udev_enum = udev_enumerate_new(udev))) {
LOG(ERROR) << "failed to obtain udev enumerate context";
return false;
}
ScopedUdevEnumerateCloser udev_enum_closer(&udev_enum);
// Populate filters for find an initialized GPIO chip.
if ((ret = udev_enumerate_add_match_subsystem(udev_enum, "gpio")) ||
(ret = udev_enumerate_add_match_sysname(udev_enum, "gpiochip*")) ||
(ret = udev_enumerate_add_match_property(udev_enum,
kIdGpioDutflaga, "*")) ||
(ret = udev_enumerate_add_match_property(udev_enum,
kIdGpioDutflagb, "*"))) {
LOG(ERROR) << "failed to initialize udev enumerate context ("
<< ret << ")";
return false;
}
// Obtain list of matching devices.
if ((ret = udev_enumerate_scan_devices(udev_enum))) {
LOG(ERROR) << "udev enumerate context scan failed (" << ret << ")";
return false;
}
// Iterate over matching devices, obtain GPIO dut_flaga identifier.
struct udev_list_entry* list_entry;
udev_list_entry_foreach(list_entry,
udev_enumerate_get_list_entry(udev_enum)) {
// Make sure we're not enumerating more than one device.
num_gpio_chips++;
if (num_gpio_chips > 1) {
LOG(WARNING) << "enumerated multiple GPIO chips, ignoring this one";
continue;
}
// Obtain device name.
const char* dev_name = udev_list_entry_get_name(list_entry);
if (!dev_name) {
LOG(WARNING) << "enumerated device has a null name string, skipping";
continue;
}
// Obtain device object.
struct udev_device* dev = udev_device_new_from_syspath(udev, dev_name);
if (!dev) {
LOG(WARNING) <<
"obtained a null device object for enumerated device, skipping";
continue;
}
// Obtain dut_flaga/b identifiers.
id_gpio_dutflaga =
udev_device_get_property_value(dev, kIdGpioDutflaga);
id_gpio_dutflagb =
udev_device_get_property_value(dev, kIdGpioDutflagb);
if (id_gpio_dutflaga && id_gpio_dutflagb) {
LOG(INFO) << "found dut_flaga/b identifiers: a=" << id_gpio_dutflaga
<< " b=" << id_gpio_dutflagb;
gpio_dutflaga_str = id_gpio_dutflaga;
gpio_dutflagb_str = id_gpio_dutflagb;
} else {
LOG(ERROR) << "GPIO chip missing dut_flaga/b properties";
}
udev_device_unref(dev);
}
}
// Obtain dut_flaga, reusing the same udev instance.
if (!dutflaga_dev_name_ && !gpio_dutflaga_str.empty()) {
LOG(INFO) << "discovering device for GPIO dut_flaga ";
if (!GetDutflagGpioDevName(udev, gpio_dutflaga_str,
&dutflaga_dev_name_)) {
LOG(ERROR) << "discovery of dut_flaga GPIO device failed";
return false;
}
}
// Now obtain dut_flagb.
if (!dutflagb_dev_name_ && !gpio_dutflagb_str.empty()) {
LOG(INFO) << "discovering device for GPIO dut_flagb";
if (!GetDutflagGpioDevName(udev, gpio_dutflagb_str,
&dutflagb_dev_name_)) {
LOG(ERROR) << "discovery of dut_flagb GPIO device failed";
return false;
}
}
LOG(INFO) << "end discovery of dut_flaga/b devices";
}
// Write cached GPIO dutflag(s) to output strings.
if (dutflaga_dev_name_p && dutflaga_dev_name_)
*dutflaga_dev_name_p = dutflaga_dev_name_;
if (dutflagb_dev_name_p && dutflagb_dev_name_)
*dutflagb_dev_name_p = dutflagb_dev_name_;
return true;
}
bool GpioHandler::GetDutflagGpioStatus(GpioHandler::DutflagGpioId id,
bool* status_p) {
CHECK(status_p);
// Obtain GPIO device file name.
string dutflag_dev_name;
switch (id) {
case kDutflagaGpio:
GetDutflagGpioDevNames(&dutflag_dev_name, NULL);
break;
case kDutflagbGpio:
GetDutflagGpioDevNames(NULL, &dutflag_dev_name);
break;
default:
LOG(FATAL) << "invalid dutflag GPIO id: " << id;
}
if (dutflag_dev_name.empty()) {
LOG(WARNING) << "could not find dutflag GPIO device";
return false;
}
// Open device for reading.
string dutflaga_value_dev_name = dutflag_dev_name + "/value";
int dutflaga_fd = HANDLE_EINTR(open(dutflaga_value_dev_name.c_str(), 0));
if (dutflaga_fd < 0) {
PLOG(ERROR) << "opening dutflaga GPIO device file failed";
return false;
}
ScopedEintrSafeFdCloser dutflaga_fd_closer(&dutflaga_fd);
// Read the dut_flaga GPIO signal. We attempt to read more than---but expect
// to receive exactly---two characters: a '0' or '1', and a newline. This is
// to ensure that the GPIO device returns a legible result.
char buf[3];
int ret = HANDLE_EINTR(read(dutflaga_fd, buf, 3));
if (ret != 2) {
if (ret < 0)
PLOG(ERROR) << "reading dutflaga GPIO status failed";
else
LOG(ERROR) << "read more than one byte (" << ret << ")";
return false;
}
// Identify and write GPIO status.
char c = buf[0];
if ((c == '0' || c == '1') && buf[1] == '\n') {
*status_p = (c == '1');
} else {
buf[2] = '\0';
LOG(ERROR) << "read unexpected value from dutflaga GPIO: " << buf;
return false;
}
return true;
}
} // namespace chromeos_update_engine