aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBertrand SIMONNET <bsimonnet@google.com>2016-03-07 14:22:09 -0800
committerBertrand Simonnet <bsimonnet@google.com>2016-03-10 21:01:57 +0000
commitd3cafe115bdde62957bd3b95fd000e6dae1b1c49 (patch)
tree05f16cb18c36f2423310fb523b0e7834bf58807b
parent0a407224a1e2b6f15fb56386f8a2633a703417e3 (diff)
downloadperipheralmanager-d3cafe115bdde62957bd3b95fd000e6dae1b1c49.tar.gz
Add a helper to acknowledge a GPIO interrupt.
When an interrupt triggers on a GPIO, we must lseek and read the value in order to be able to poll again. As we don't guarantee that the file descriptor we use to poll will be the value file, add a convenience function to reset the file descriptor's state. To illustrate how to use it, we provide an example that will watch a GPIO for interrupt and print a message when the GPIO's value changes. This is implemented with both select and poll. Bug: 26778811 Change-Id: Ib25338599a8e08d7734839171da6204bb64a2ded
-rw-r--r--client/gpio_impl.cc3
-rw-r--r--client/wrapper.cc7
-rw-r--r--example/Android.mk27
-rw-r--r--example/gpio_flip_example.cc (renamed from example/peripheralmanager_example.cc)0
-rw-r--r--example/gpio_interrupt_example.cc114
-rw-r--r--include/peripheralmanager/gpio.h7
6 files changed, 155 insertions, 3 deletions
diff --git a/client/gpio_impl.cc b/client/gpio_impl.cc
index 383b2b5..cf57613 100644
--- a/client/gpio_impl.cc
+++ b/client/gpio_impl.cc
@@ -108,6 +108,9 @@ int GpioImpl::GetPollingFd(int* fd) {
ScopedFd scoped_fd;
Status ret = client_->GetGpioPollingFd(name_, &scoped_fd);
if (ret.isOk()) {
+ uint8_t buf[2];
+ read(scoped_fd.get(), buf, 2);
+ lseek(scoped_fd.get(), 0, SEEK_SET);
*fd = scoped_fd.release();
}
return ret.serviceSpecificErrorCode();
diff --git a/client/wrapper.cc b/client/wrapper.cc
index 82e74a1..2e08dca 100644
--- a/client/wrapper.cc
+++ b/client/wrapper.cc
@@ -118,6 +118,13 @@ int BGpio_getPollingFd(const BGpio* gpio, int* fd) {
return gpio->impl->GetPollingFd(fd);
}
+int BGpio_ackInterruptEvent(int fd) {
+ uint8_t buf[2];
+ lseek(fd, 0, SEEK_SET);
+ read(fd, buf, 2);
+ return 0;
+}
+
void BGpio_delete(BGpio* gpio) {
delete gpio->impl;
delete gpio;
diff --git a/example/Android.mk b/example/Android.mk
index c3e1ddd..56b5cb0 100644
--- a/example/Android.mk
+++ b/example/Android.mk
@@ -16,11 +16,11 @@
LOCAL_PATH := $(call my-dir)
-# peripheralmanager_example executable
+# Flips a GPIO on and off.
# ========================================================
include $(CLEAR_VARS)
-LOCAL_MODULE := peripheralmanager_example
+LOCAL_MODULE := peripheralman_gpio_flip
LOCAL_CPP_EXTENSION := .cc
LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
LOCAL_CFLAGS += -Wno-sign-promo # for libchrome
@@ -33,7 +33,28 @@ LOCAL_SHARED_LIBRARIES := \
libutils \
LOCAL_SRC_FILES := \
- peripheralmanager_example.cc \
+ gpio_flip_example.cc \
+
+include $(BUILD_EXECUTABLE)
+
+# Watches the state of a gpio with interrupts.
+# ========================================================
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := peripheralman_gpio_interrupt
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
+LOCAL_CFLAGS += -Wno-sign-promo # for libchrome
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libbinderwrapper \
+ libbrillo \
+ libchrome \
+ libperipheralman \
+ libutils \
+
+LOCAL_SRC_FILES := \
+ gpio_interrupt_example.cc \
include $(BUILD_EXECUTABLE)
diff --git a/example/peripheralmanager_example.cc b/example/gpio_flip_example.cc
index 36f5979..36f5979 100644
--- a/example/peripheralmanager_example.cc
+++ b/example/gpio_flip_example.cc
diff --git a/example/gpio_interrupt_example.cc b/example/gpio_interrupt_example.cc
new file mode 100644
index 0000000..e681fa5
--- /dev/null
+++ b/example/gpio_interrupt_example.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2016 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 <poll.h>
+#include <sys/select.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <base/at_exit.h>
+#include <base/logging.h>
+#include <base/message_loop/message_loop.h>
+#include <base/sys_info.h>
+#include <base/time/time.h>
+#include <binderwrapper/binder_wrapper.h>
+#include <brillo/flag_helper.h>
+
+#include <peripheralmanager/peripheral_manager_client.h>
+
+int main(int argc, char* argv[]) {
+ DEFINE_string(pin, "", "Pin to watch");
+ DEFINE_bool(select, false, "Use select instead of poll");
+ brillo::FlagHelper::Init(argc, argv, "Watches a GPIO.");
+ logging::InitLogging(logging::LoggingSettings());
+ android::BinderWrapper::Create();
+
+ std::string pin_name = "IO1";
+ if (!FLAGS_pin.empty())
+ pin_name = FLAGS_pin;
+
+ // Get a client to the PeripheralManager.
+ BPeripheralManagerClient* client = BPeripheralManagerClient_new();
+
+ if (!client) {
+ LOG(ERROR) << "Failed to connect to client";
+ return 1;
+ }
+
+ // Open GPIO pin.
+ BGpio* my_gpio;
+ if (BPeripheralManagerClient_openGpio(client, pin_name.c_str(), &my_gpio)) {
+ LOG(ERROR) << "Failed to open Gpio";
+ return 1;
+ }
+
+ // Set the direction to in.
+ if (BGpio_setDirection(my_gpio, DIRECTION_IN)) {
+ LOG(ERROR) << "Failed to set gpio pin direction";
+ return 1;
+ }
+
+ // Set the edge trigger type for interrupts.
+ if (BGpio_setEdgeTriggerType(my_gpio, BOTH_EDGE)) {
+ LOG(ERROR) << "failed to set the edge type";
+ return 1;
+ }
+
+ // Get the file descriptor used to poll for data.
+ int fd = -1;
+ if (BGpio_getPollingFd(my_gpio, &fd)) {
+ LOG(ERROR) << "failed to get the fd";
+ return 1;
+ }
+
+ if (FLAGS_select) {
+ fd_set ifds;
+ FD_ZERO(&ifds);
+ FD_SET(fd, &ifds);
+
+ for (int i = 0; i < 30; i++) {
+ select(fd + 1, NULL, NULL, &ifds, NULL);
+ LOG(INFO) << "received event";
+ BGpio_ackInterruptEvent(fd);
+ }
+ } else {
+ struct pollfd poller = {
+ .fd = fd,
+ .events = POLLPRI | POLLERR,
+ .revents = 0,
+ };
+
+ for (int i = 0; i < 30; i++) {
+ // Poll with no timeout.
+ poll(&poller, 1, -1);
+ LOG(INFO) << "received an event ";
+ BGpio_ackInterruptEvent(fd);
+ poller.revents = 0;
+ }
+ }
+
+ close(fd);
+
+ // Release the gpio pin
+ BGpio_delete(my_gpio);
+
+ // Close the connection to PeripheralManager.
+ BPeripheralManagerClient_delete(client);
+
+ LOG(INFO) << "Exiting";
+ return 0;
+}
diff --git a/include/peripheralmanager/gpio.h b/include/peripheralmanager/gpio.h
index 28989ea..de5bfdd 100644
--- a/include/peripheralmanager/gpio.h
+++ b/include/peripheralmanager/gpio.h
@@ -91,6 +91,13 @@ int BGpio_getValue(const BGpio* gpio, int* value);
/// @return Error code (one of peripheral_error_t).
int BGpio_getPollingFd(const BGpio* gpio, int* fd);
+/// Acknowledges the interrupt and resets the file descriptor.
+/// This must be called after each event triggers in order to be able to
+/// poll/select for another event.
+/// @param fd Polling file descriptor to reset.
+/// @return 0 on success, errno on error.
+int BGpio_ackInterruptEvent(int fd);
+
/// Destroys a BGpio struct.
/// @param gpio Pointer to the BGpio struct.
void BGpio_delete(BGpio* gpio);