blob: 2e6319d7d4f727e6c98d9a0de9c0d6e73af523fd [file] [log] [blame]
/* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string>
#include <sys/poll.h>
#include <sys/signalfd.h>
#include <sys/timerfd.h>
#include <base/files/file_path.h>
#include <base/format_macros.h>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <brillo/flag_helper.h>
#include <brillo/streams/file_stream.h>
#include <brillo/streams/stream_utils.h>
// Define defaults that work on the Dragonboard 410c.
const int kDefaultGpioPortOffset = 902;
const int kDefaultGpioOut = 24;
const int kDefaultGpioIn = 28;
// GPIO sysfs path
const char* const kGPIOSysfsPath = "/sys/class/gpio";
// GPIO polling timeout in milliseconds
const int kPollTimeoutMsecs = 3000; /* 3 seconds */
const int kBufSize = 10;
namespace {
/*
* Get a file stream for GPIO export
* bool write: access mode with true for write
* return: file stream pointer
*/
brillo::StreamPtr GetGPIOExportStream(bool write) {
std::string gpio_path;
gpio_path = base::StringPrintf("%s/export", kGPIOSysfsPath);
base::FilePath dev_path{gpio_path};
auto access_mode = brillo::stream_utils::MakeAccessMode(!write, write);
return brillo::FileStream::Open(
dev_path, access_mode, brillo::FileStream::Disposition::OPEN_EXISTING,
nullptr);
}
/*
* Get a file stream for GPIO data
* int gpio: GPIO pin number
* std::string type: GPIO type e.g. direction, edge, value
* bool write: access mode with true for write
* return: file stream pointer
*/
brillo::StreamPtr GetGPIODataStream(int gpio, const std::string type, bool write) {
std::string gpio_path;
gpio_path = base::StringPrintf("%s/gpio%d/%s", kGPIOSysfsPath, gpio, type.c_str());
base::FilePath dev_path{gpio_path};
auto access_mode = brillo::stream_utils::MakeAccessMode(!write, write);
return brillo::FileStream::Open(
dev_path, access_mode, brillo::FileStream::Disposition::OPEN_EXISTING,
nullptr);
}
} // anonymous namespace
/*
* Export GPIO to user space
* int gpio: GPIO pin number
*/
void export_gpio(int gpio) {
brillo::StreamPtr stream = GetGPIOExportStream(true);
if (!stream)
return;
std::string value = base::StringPrintf("%d", gpio);
stream->WriteAllBlocking(value.data(), value.size(), nullptr);
}
/*
* Write to GPIO
* std::string type: what GPIO type i.e. direction, value, edge
* std::string v: value to be written
* return: true
*/
bool write_gpio(int gpio, const std::string type, std::string v) {
brillo::StreamPtr stream = GetGPIODataStream(gpio, type, true);
if (!stream)
return false;
stream->WriteAllBlocking(v.data(), v.size(), nullptr);
return true;
}
/*
* This method polls GPIO pin for 0 -> 1 transition.
* int gpio: GPIO pin to be polled.
* return:
* true if polling reads back successfully
* false if GPIO open error or polling error
*/
bool poll_gpio(int gpio) {
char buf[kBufSize];
struct pollfd fdset[1];
int nfds = 1;
int fd = -1;
int rc = -1;
int len = -1;
std::string gpio_path;
gpio_path = base::StringPrintf("%s/gpio%d/value", kGPIOSysfsPath, gpio);
fd = open(gpio_path.c_str(), O_RDONLY | O_NONBLOCK );
if (fd < 0) {
PLOG(ERROR) << "GPIO open error";
return false;
}
// Use this sawZeroValue to ignore the first spurious interrupt.
bool sawZeroValue = false;
while(1) {
memset((void*)fdset, 0, sizeof(fdset));
fdset[0].fd = fd;
fdset[0].events = POLLPRI;
rc = poll(fdset, nfds, kPollTimeoutMsecs);
if (rc < 0) {
PLOG(ERROR) << "poll() failed!";
close(fd);
return false;
} else if (rc == 0) {
LOG(INFO) << "polling...";
} else if (fdset[0].revents & POLLPRI) {
len = read(fdset[0].fd, buf, kBufSize - 1);
lseek(fdset[0].fd, 0, SEEK_SET);
if (len >= 0) {
buf[len] = '\0';
}
// TODO: http://b/25317933 Handle the case where read()
// might result in multiple events being read.
if (buf[0] == '0') {
LOG(INFO) << "First interrupt with value 0 ignored.";
sawZeroValue = true;
continue;
}
if (sawZeroValue && buf[0] == '1') {
LOG(INFO) << "GPIO pin reset and interrupt with value 1 caught.";
close(fd);
return true;
}
}
}
close(fd);
return true;
}
int main(int argc, char **argv) {
int gpio_out = -1;
int gpio_in = -1;
int goffset = kDefaultGpioPortOffset;
DEFINE_int32(gin, 28, "GPIO input");
DEFINE_int32(gout, 24, "GPIO output");
DEFINE_int32(goffset, 902, "GPIO port offset for Dragonboard");
brillo::FlagHelper::Init(argc, argv, "GPIO Playground.");
if (argc == 1 || argc == 3 || argc == 4) {
if (FLAGS_goffset) {
goffset = FLAGS_goffset;
}
gpio_out = FLAGS_gout + goffset;
gpio_in = FLAGS_gin + goffset;
} else {
printf("Pass input & output GPIO pin as follows\n");
printf("adb shell '/system/bin/gpio_playground --gin=28 --gout=24 --goffset=902 &'\n");
return -1;
}
LOG(INFO) << "Export pin " << kDefaultGpioOut << " to userspace";
export_gpio(gpio_out);
LOG(INFO) << "Set pin " << kDefaultGpioOut << " direction to out";
write_gpio(gpio_out, "direction", "out");
LOG(INFO) << "Set pin " << kDefaultGpioOut << " value to 1";
write_gpio(gpio_out, "value", "1");
LOG(INFO) << "Export pin " << kDefaultGpioIn << " to userspace";
export_gpio(gpio_in);
LOG(INFO) << "Set pin " << kDefaultGpioIn << " direction to in";
write_gpio(gpio_in, "direction", "in");
LOG(INFO) << "Set pin " << kDefaultGpioIn << " edge to both";
write_gpio(gpio_in, "edge", "both");
if (poll_gpio(gpio_in)) {
LOG(INFO) << "Pin " << kDefaultGpioIn << " pushed. Now turn off LED";
write_gpio(gpio_out, "value", "0");
}
return 0;
}