| /* 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; |
| } |