如何使用 poll(2) 或 select(2) 服务调用来观察伪文件以了解 Kotlin 的更改
how to use poll(2) or select(2) service call to watch a pseudo file for changes with Kotlin
我正在使用 DragonBoard 410C 使用 Android 5.1 和 Kotlin 来试验 40 针低功率连接器上的 GPIO 针。我正在使用的库使用 sysfs 接口与 GPIO 引脚交互,这需要在 /sys/class/gpio/
目录树中打开各种伪文件并向这些文件读取和写入值,请参阅
我的理解是,我可以提供一个 GPIO 引脚作为输入和边沿触发,这将允许我连接一个带有瞬时接触开关的简单电路,并能够检测何时按下开关。
但是我找到的文档表明我需要在文件描述符上使用 poll(2) system service or the select(2) 系统服务来检测我正在使用的 GPIO 引脚的 /value
伪文件当检测到边缘时,例如/sys/class/gpio/gpio910/value
.
如何在 Kotlin 中使用带有文件描述符的 poll(2)
或 select(2)
系统服务? poll(2)
和FileReader
的ready()
方法一样吗?
也许需要类似于 Java WatchService
功能的东西? http://www.java2s.com/Tutorials/Java/java.nio.file/WatchService/0060__WatchService.poll_.htm
除非这是错误的方法,否则我正在计划的是具有效用函数,例如:
// pollPseudoFile() polls the specified GPIO pin using the sysfs interface in order
// to wait for a pin value due to an external event such as a momentary switch press
//
// Returns:
// - 0 -> an error of some kind
// - 1 -> time out expired with no value
// - 2 -> value change detected
public fun pollPseudoFile (pinPathFull : String, timeOut : Int) : Int {
println(" pollPseudoFile - String")
var iStatus : Int = 0 // status return indicating if poll() completed or not.
try {
val br = FileReader(pinPathFull)
br.poll(timeOut) // wait for data to be available or time out. how to do poll?
iStatus = 2 // indicate value change unless the poll() timed out
if (br.pollExpired) iStatus = 1 // poll timed out. how to check for poll status?
br.close()
} catch (e: Exception) {
println("Error: " + e.message)
}
return iStatus;
}
public fun pollGetValue (pinPathFull : String) : Int {
println(" pollGetValue - String")
var line = ""
try {
val br = BufferedReader(FileReader(pinPathFull))
line = br.readLine()
br.close()
} catch (e: Exception) {
println("Error: " + e.message)
}
return line.toInt()
}
https://www.kernel.org/doc/Documentation/gpio/sysfs.txt
"value" ... reads as either 0 (low) or 1 (high). If the GPIO is
configured as an output, this value may be written; any nonzero
value is treated as high.
If the pin can be configured as interrupt-generating interrupt and
if it has been configured to generate interrupts (see the
description of "edge"), you can poll(2) on that file and poll(2)
will return whenever the interrupt was triggered. If you use
poll(2), set the events POLLPRI and POLLERR. If you use select(2),
set the file descriptor in exceptfds. After poll(2) returns, either
lseek(2) to the beginning of the sysfs file and read the new value
or close the file and re-open it to read the value.
"edge" ... reads as either "none", "rising", "falling", or "both".
Write these strings to select the signal edge(s) that will make
poll(2) on the "value" file return.
This file exists only if the pin can be configured as an interrupt
generating input pin.
补充说明
注 1: 使用 adb
实用程序,我能够 shell
进入我的 DragonBoard 410C 并测试配置物理引脚 26,GPIO971,使用 direction
设置为 in
,edge
设置为 rising
。在连接到物理引脚 23 GPIO938 的面包板上使用一个简单的 LED 电路,并将物理引脚 26 的电线添加到由物理引脚 23 管理的 LED,我能够使用 echo 1 > gpio938/value
和然后 cat gpio971/value
查看物理引脚 26 的值变高并且读数为 1
。然后我用 echo 0 > gpio938/value
关闭了连接到物理引脚 23 的 LED,然后 cat gpio971/value
return 按预期输入了 0
的值。
但是这个实验并没有告诉我 poll(2)
是否会指示 gpio971/value
在 LED 打开和关闭时发生变化。
注 1a: 我有第一个版本的原生 C++ JNI 函数来实现 poll(2)
服务调用,并且一直在用我的 DragonBoard 410C 对其进行测试。我所看到的是 poll(2)
函数立即 returning POLLIN
和 POLLERR
设置在 struct pollfd
的 revents
成员中数组。
测试使用物理引脚 26 连接到面包板行,LED 的一条腿连接到物理引脚 23,我可以打开和关闭它。当我尝试以 10000 毫秒超时打开轮询时,无论 LED 是否点亮(引脚 26 值为 1)或不点亮(引脚 26 值为 0),都会立即调用 returns 并设置两个指示灯。
我的期望是,因为我将 edge
设置为 rising
,我应该只在 LED 未点亮时看到 poll(2)
return 然后我将其打开打开或已过去 10 秒。
我正在继续调查,因为它让我印象深刻,我在应用程序的 Kotlin 端编写的本机 C++ 函数的使用方式可能存在问题。
注意 2: 我试图在我的 Kotlin 应用程序中使用 WatchService
,运行 出现错误,WatchService
需要一个 API 26 级,我在 Android Studio 中的最低目标是 API 22 级。看起来 WatchService
需要 Android 8.0 (Oreo) 而 DragonBoard 在 Android 5.1 (Lollipop) 所以我无法使用 WatchService
来监控文件状态。
我采用的方法是创建一个本机 C++ JNI 函数来提供一种实现 poll(2) Linux service call.
的方法
我 运行 在开发和测试期间遇到的一个有趣问题是 poll()
立即返回,而不是等待超时或 GPIO 输入引脚的电压。在 DragonBoard 410C 的 96Boards.org 论坛上发帖后,How to use poll() with sysfs interface to input GPIO pin to handle a switch press event,有人提出了一个可行的解决方案,即在开始轮询之前读取伪文件(2)。
为了使用此功能,我需要某种 Kotlin 协程或侧线程,以便当主程序 UI 正在处理按钮点击时开始轮询 GPIO 输入引脚,主 UI 线程不会被阻塞,直到函数 returns 出现 GPIO 事件或超时。
我还不能辨别如何执行这样的协程,所以这项工作仍在进行中。经过一番思考,某种事件侦听器架构似乎是最合适的方法。
但是测试表明,当使用 1.8v 的电线手动施加电压时,函数 pollPseudoFile()
通过超时或返回 /value
的值正常工作为 GPIO 输入引脚供电(引脚 38),在 /edge
伪文件中使用 rising
或 falling
设置进行设置。
本机C++ JNI函数的源代码如下。我将它与以下 Kotlin 源代码一起使用。
首先,在我的 MainActivity.kt
源文件中,我使用以下源提供了本机 C++ 库:
// See the Whosebug question with answer at URL:
//
init {
System.loadLibrary("pollfileservice")
}
external fun pollFileWithTimeOut(pathPseudo : String, timeOutMs : Int): Int
external fun pollGetLastRevents() : Int
接下来,我将在 Kotlin 源文件 Gpio.kt
中使用此函数来实际执行对伪文件的 poll()
服务调用。
class Gpio(pin: Int) {
private val pin : Int
private val pinGpio : GpioFile = GpioFile()
/*
* The GPIO pins are represented by folders in the Linux file system
* within the folder /sys/class/gpio. Each pin is represented by a folder
* whose name is the prefix "gpio" followed by the pin number.
* Within the folder representing the pin are two files, "value" used to
* set or get the value of the pin and "direction" used to set or get
* the direction of the pin.
*
* This function creates the path to the Linux file which represents a particular
* GPIO pin function, "value" or "direction".
*/
private fun MakeFileName(pin: Int, op: String): String {
return "/sys/class/gpio/gpio$pin$op"
}
// ....... other source code in the Kotlin class Gpio
fun pinPoll (timeMs: Int) : Int {
val iStatus : Int = pinGpio.pollPseudoFile (MakeFileName(pin, "/value"), timeMs)
return iStatus
}
上面的Gpio class在实际的UI按钮点击监听中使用如下:
val gpioProcessor = GpioProcessor()
// Get reference of GPIO23.
val gpioPin26 = gpioProcessor.pin26
// Set GPIO26 as input.
gpioPin26.pinIn()
gpioPin26.pinEdgeRising()
var xStatus: Int = gpioPin26.pinPoll(10000)
val xvalue = gpioPin26.value
PollFileService.h
//
// Created by rchamber on 9/24/2020.
//
#ifndef MY_APPLICATION_POLLFILESERVICE_H
#define MY_APPLICATION_POLLFILESERVICE_H
class PollFileService {
private:
int iValue;
int fd; /* file descriptor */
public:
// See poll(2) man page at https://linux.die.net/man/2/poll
static const int PollSuccess = 0;
static const int PollTimeOut = 1;
static const int PollErrorEFAULT = -1;
static const int PollErrorEINTR = -2;
static const int PollErrorEINVAL = -3;
static const int PollErrorENOMEM = -4;
static const int PollErrorPOLLERR = -5;
static const int PollErrorPOLLNVAL = -6;
static const int PollErrorPOLLERRNVAL = -7;
static const int PollErrorPOLLHUP = -8;
static const int PollErrorPOLLERRDEFLT = -9;
static const int PollErrorUNKNOWN = -100;
static int iPollStatus;
static int iPollRet;
static int iPollRevents;
PollFileService(const char *pathName = nullptr, int timeMilliSec = -1);
~PollFileService();
int PollFileCheck (const char *pathName, int timeMilliSec = -1);
int PollFileRead (const char *pathName = nullptr);
};
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_myapplication_MainActivity_pollFileWithTimeOut (JNIEnv* pEnv, jobject pThis, jstring pKey, jint timeMS);
#endif //MY_APPLICATION_POLLFILESERVICE_H
PollFileService.cpp
//
// Created by rchamber on 9/24/2020.
//
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <math.h>
#include <errno.h>
#include <poll.h>
#include <jni.h>
#include "PollFileService.h"
int PollFileService::iPollStatus = 0;
int PollFileService::iPollRet = 0;
int PollFileService::iPollRevents = 0;
PollFileService::PollFileService(const char *pathName /* = nullptr */, int timeMilliSec /* = -1 */) : iValue(23), fd(-1)
{
iPollStatus = 0;
if (pathName) {
fd = open (pathName, O_RDONLY);
}
}
PollFileService::~PollFileService()
{
if (fd >= 0) {
close (fd);
fd = -1;
}
}
int PollFileService::PollFileCheck(const char *pathName, int timeMilliSec /* = -1 */)
{
struct pollfd fdList[] = {
{fd, POLLPRI | POLLERR, 0},
{0}
};
nfds_t nfds = 1;
unsigned char tempbuff[256] = {0};
if (fd < 0 && pathName) {
fd = open (pathName, O_RDONLY);
fdList[0].fd = fd;
}
// with a edge triggered GPIO that we are going to use the poll(2)
// function to wait on an event, we need to read from the
// pin before we do the poll(2). If the read is not done then
// the poll(2) returns with both POLLPRI and POLLERR set in the
// revents member. however if we read first then do the poll2()
// the poll(2) will wait for the event, input voltage change with
// either a rising edge or a falling edge, depending on the setting
// in the /edge pseudo file.
ssize_t iCount = read (fdList[0].fd, tempbuff, 255);
iPollStatus = PollErrorUNKNOWN;
int iRet = poll(fdList, nfds, timeMilliSec);
if (iRet == 0) {
iPollStatus = PollTimeOut;
} else if (iRet < 0) {
switch (errno) {
case EFAULT:
iPollStatus = PollErrorEFAULT;
break;
case EINTR:
iPollStatus = PollErrorEINTR;
break;
case EINVAL:
iPollStatus = PollErrorEINVAL;
break;
case ENOMEM:
iPollStatus = PollErrorENOMEM;
break;
default:
iPollStatus = PollErrorUNKNOWN;
break;
}
} else if (iRet > 0) {
// successful call now determine what we should return.
iPollRevents = fdList[0].revents; /* & (POLLIN | POLLPRI | POLLERR); */
switch (fdList[0].revents & (POLLIN | POLLPRI | POLLERR /* | POLLNVAL | POLLHUP*/)) {
case (POLLIN): // value of 1, There is data to read.
case (POLLPRI): // value of 2, There is urgent data to read
case (POLLOUT): // , Writing now will not block.
case (POLLIN | POLLPRI): // value of 3
iPollStatus = PollSuccess;
break;
// testing with a DragonBoard 410C indicates that we may
// see the POLLERR indicator set in revents along with
// the POLLIN and/or POLLPRI indicator set indicating there
// is data to be read.
// see as well poll(2) man page which states:
// POLLERR Error condition (output only).
case (POLLIN | POLLERR): // value of 9
case (POLLPRI | POLLERR): // value of 10
case (POLLIN | POLLPRI | POLLERR): // value of 11
iPollStatus = PollSuccess;
break;
case (POLLHUP): // , Hang up (output only).
iPollStatus = PollErrorPOLLHUP;
break;
case (POLLERR): // value of 8, Error condition (output only).
iPollStatus = PollErrorPOLLERR;
break;
case (POLLNVAL): // , Invalid request: fd not open (output only).
iPollStatus = PollErrorPOLLNVAL;
break;
case (POLLERR | POLLNVAL):
iPollStatus = PollErrorPOLLERRNVAL;
break;
default:
iPollStatus = PollErrorPOLLERRDEFLT;
break;
}
}
return iPollStatus;
}
int PollFileService::PollFileRead (const char *pathName /* = nullptr */)
{
char buffer[12] = {0};
int iRet = -1;
if (fd < 0 && pathName) {
fd = open (pathName, O_RDONLY);
}
int nCount = read (fd, buffer, 10);
if (nCount > 0) {
iRet = atoi (buffer);
}
return iRet;
}
// Check the specified file using the poll(2) service and
// return a status as follows:
// - 0 -> poll(2) success indicating something is available
// - 1 -> poll(2) failed with time out before anything available
// - -1 -> poll(2) error - EFAULT
// - -2 -> poll(2) error - EINTR
// - -3 -> poll(2) error - EINVAL
// - -4 -> poll(2) error - ENOMEM
// - -5 -> poll(2) error - POLLERR
// - -6 -> poll(2) error - POLLNVAL
// - -7 -> poll(2) error - POLLERR | POLLNVAL
// - -8 -> poll(2) error - POLLHUP
// - -9 -> poll(2) error - poll(2) revent indicator Unknown
// - -100 -> poll(2) error - Unknown error
//
static int lastRevents = 0;
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_myapplication_MainActivity_pollFileWithTimeOut (JNIEnv* pEnv, jobject pThis, jstring pKey, jint timeMS)
{
char *pathName;
int timeMilliSec;
PollFileService myPoll;
const char *str = pEnv->GetStringUTFChars(pKey, 0);
int timeMSint = 10000; // timeMS;
#if 1
int iStatus = myPoll.PollFileCheck(str, timeMSint);
#else
int iStatus = myPoll.PollFileRead(str);
#endif
pEnv->ReleaseStringUTFChars(pKey, str);
lastRevents = myPoll.iPollRevents;
return iStatus;
}
#if 0
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_myapplication_MainActivity_pollGetLastStatus (JNIEnv* pEnv, jobject pThis) {
return PollFileService::iPollStatus;
}
#endif
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_myapplication_MainActivity_pollGetLastRevents (JNIEnv* pEnv, jobject pThis)
{
return lastRevents;
}
我正在使用 DragonBoard 410C 使用 Android 5.1 和 Kotlin 来试验 40 针低功率连接器上的 GPIO 针。我正在使用的库使用 sysfs 接口与 GPIO 引脚交互,这需要在 /sys/class/gpio/
目录树中打开各种伪文件并向这些文件读取和写入值,请参阅
我的理解是,我可以提供一个 GPIO 引脚作为输入和边沿触发,这将允许我连接一个带有瞬时接触开关的简单电路,并能够检测何时按下开关。
但是我找到的文档表明我需要在文件描述符上使用 poll(2) system service or the select(2) 系统服务来检测我正在使用的 GPIO 引脚的 /value
伪文件当检测到边缘时,例如/sys/class/gpio/gpio910/value
.
如何在 Kotlin 中使用带有文件描述符的 poll(2)
或 select(2)
系统服务? poll(2)
和FileReader
的ready()
方法一样吗?
也许需要类似于 Java WatchService
功能的东西? http://www.java2s.com/Tutorials/Java/java.nio.file/WatchService/0060__WatchService.poll_.htm
除非这是错误的方法,否则我正在计划的是具有效用函数,例如:
// pollPseudoFile() polls the specified GPIO pin using the sysfs interface in order
// to wait for a pin value due to an external event such as a momentary switch press
//
// Returns:
// - 0 -> an error of some kind
// - 1 -> time out expired with no value
// - 2 -> value change detected
public fun pollPseudoFile (pinPathFull : String, timeOut : Int) : Int {
println(" pollPseudoFile - String")
var iStatus : Int = 0 // status return indicating if poll() completed or not.
try {
val br = FileReader(pinPathFull)
br.poll(timeOut) // wait for data to be available or time out. how to do poll?
iStatus = 2 // indicate value change unless the poll() timed out
if (br.pollExpired) iStatus = 1 // poll timed out. how to check for poll status?
br.close()
} catch (e: Exception) {
println("Error: " + e.message)
}
return iStatus;
}
public fun pollGetValue (pinPathFull : String) : Int {
println(" pollGetValue - String")
var line = ""
try {
val br = BufferedReader(FileReader(pinPathFull))
line = br.readLine()
br.close()
} catch (e: Exception) {
println("Error: " + e.message)
}
return line.toInt()
}
https://www.kernel.org/doc/Documentation/gpio/sysfs.txt
"value" ... reads as either 0 (low) or 1 (high). If the GPIO is configured as an output, this value may be written; any nonzero value is treated as high.
If the pin can be configured as interrupt-generating interrupt and if it has been configured to generate interrupts (see the description of "edge"), you can poll(2) on that file and poll(2) will return whenever the interrupt was triggered. If you use poll(2), set the events POLLPRI and POLLERR. If you use select(2), set the file descriptor in exceptfds. After poll(2) returns, either lseek(2) to the beginning of the sysfs file and read the new value or close the file and re-open it to read the value.
"edge" ... reads as either "none", "rising", "falling", or "both". Write these strings to select the signal edge(s) that will make poll(2) on the "value" file return.
This file exists only if the pin can be configured as an interrupt generating input pin.
补充说明
注 1: 使用 adb
实用程序,我能够 shell
进入我的 DragonBoard 410C 并测试配置物理引脚 26,GPIO971,使用 direction
设置为 in
,edge
设置为 rising
。在连接到物理引脚 23 GPIO938 的面包板上使用一个简单的 LED 电路,并将物理引脚 26 的电线添加到由物理引脚 23 管理的 LED,我能够使用 echo 1 > gpio938/value
和然后 cat gpio971/value
查看物理引脚 26 的值变高并且读数为 1
。然后我用 echo 0 > gpio938/value
关闭了连接到物理引脚 23 的 LED,然后 cat gpio971/value
return 按预期输入了 0
的值。
但是这个实验并没有告诉我 poll(2)
是否会指示 gpio971/value
在 LED 打开和关闭时发生变化。
注 1a: 我有第一个版本的原生 C++ JNI 函数来实现 poll(2)
服务调用,并且一直在用我的 DragonBoard 410C 对其进行测试。我所看到的是 poll(2)
函数立即 returning POLLIN
和 POLLERR
设置在 struct pollfd
的 revents
成员中数组。
测试使用物理引脚 26 连接到面包板行,LED 的一条腿连接到物理引脚 23,我可以打开和关闭它。当我尝试以 10000 毫秒超时打开轮询时,无论 LED 是否点亮(引脚 26 值为 1)或不点亮(引脚 26 值为 0),都会立即调用 returns 并设置两个指示灯。
我的期望是,因为我将 edge
设置为 rising
,我应该只在 LED 未点亮时看到 poll(2)
return 然后我将其打开打开或已过去 10 秒。
我正在继续调查,因为它让我印象深刻,我在应用程序的 Kotlin 端编写的本机 C++ 函数的使用方式可能存在问题。
注意 2: 我试图在我的 Kotlin 应用程序中使用 WatchService
,运行 出现错误,WatchService
需要一个 API 26 级,我在 Android Studio 中的最低目标是 API 22 级。看起来 WatchService
需要 Android 8.0 (Oreo) 而 DragonBoard 在 Android 5.1 (Lollipop) 所以我无法使用 WatchService
来监控文件状态。
我采用的方法是创建一个本机 C++ JNI 函数来提供一种实现 poll(2) Linux service call.
的方法我 运行 在开发和测试期间遇到的一个有趣问题是 poll()
立即返回,而不是等待超时或 GPIO 输入引脚的电压。在 DragonBoard 410C 的 96Boards.org 论坛上发帖后,How to use poll() with sysfs interface to input GPIO pin to handle a switch press event,有人提出了一个可行的解决方案,即在开始轮询之前读取伪文件(2)。
为了使用此功能,我需要某种 Kotlin 协程或侧线程,以便当主程序 UI 正在处理按钮点击时开始轮询 GPIO 输入引脚,主 UI 线程不会被阻塞,直到函数 returns 出现 GPIO 事件或超时。
我还不能辨别如何执行这样的协程,所以这项工作仍在进行中。经过一番思考,某种事件侦听器架构似乎是最合适的方法。
但是测试表明,当使用 1.8v 的电线手动施加电压时,函数 pollPseudoFile()
通过超时或返回 /value
的值正常工作为 GPIO 输入引脚供电(引脚 38),在 /edge
伪文件中使用 rising
或 falling
设置进行设置。
本机C++ JNI函数的源代码如下。我将它与以下 Kotlin 源代码一起使用。
首先,在我的 MainActivity.kt
源文件中,我使用以下源提供了本机 C++ 库:
// See the Whosebug question with answer at URL:
//
init {
System.loadLibrary("pollfileservice")
}
external fun pollFileWithTimeOut(pathPseudo : String, timeOutMs : Int): Int
external fun pollGetLastRevents() : Int
接下来,我将在 Kotlin 源文件 Gpio.kt
中使用此函数来实际执行对伪文件的 poll()
服务调用。
class Gpio(pin: Int) {
private val pin : Int
private val pinGpio : GpioFile = GpioFile()
/*
* The GPIO pins are represented by folders in the Linux file system
* within the folder /sys/class/gpio. Each pin is represented by a folder
* whose name is the prefix "gpio" followed by the pin number.
* Within the folder representing the pin are two files, "value" used to
* set or get the value of the pin and "direction" used to set or get
* the direction of the pin.
*
* This function creates the path to the Linux file which represents a particular
* GPIO pin function, "value" or "direction".
*/
private fun MakeFileName(pin: Int, op: String): String {
return "/sys/class/gpio/gpio$pin$op"
}
// ....... other source code in the Kotlin class Gpio
fun pinPoll (timeMs: Int) : Int {
val iStatus : Int = pinGpio.pollPseudoFile (MakeFileName(pin, "/value"), timeMs)
return iStatus
}
上面的Gpio class在实际的UI按钮点击监听中使用如下:
val gpioProcessor = GpioProcessor()
// Get reference of GPIO23.
val gpioPin26 = gpioProcessor.pin26
// Set GPIO26 as input.
gpioPin26.pinIn()
gpioPin26.pinEdgeRising()
var xStatus: Int = gpioPin26.pinPoll(10000)
val xvalue = gpioPin26.value
PollFileService.h
//
// Created by rchamber on 9/24/2020.
//
#ifndef MY_APPLICATION_POLLFILESERVICE_H
#define MY_APPLICATION_POLLFILESERVICE_H
class PollFileService {
private:
int iValue;
int fd; /* file descriptor */
public:
// See poll(2) man page at https://linux.die.net/man/2/poll
static const int PollSuccess = 0;
static const int PollTimeOut = 1;
static const int PollErrorEFAULT = -1;
static const int PollErrorEINTR = -2;
static const int PollErrorEINVAL = -3;
static const int PollErrorENOMEM = -4;
static const int PollErrorPOLLERR = -5;
static const int PollErrorPOLLNVAL = -6;
static const int PollErrorPOLLERRNVAL = -7;
static const int PollErrorPOLLHUP = -8;
static const int PollErrorPOLLERRDEFLT = -9;
static const int PollErrorUNKNOWN = -100;
static int iPollStatus;
static int iPollRet;
static int iPollRevents;
PollFileService(const char *pathName = nullptr, int timeMilliSec = -1);
~PollFileService();
int PollFileCheck (const char *pathName, int timeMilliSec = -1);
int PollFileRead (const char *pathName = nullptr);
};
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_myapplication_MainActivity_pollFileWithTimeOut (JNIEnv* pEnv, jobject pThis, jstring pKey, jint timeMS);
#endif //MY_APPLICATION_POLLFILESERVICE_H
PollFileService.cpp
//
// Created by rchamber on 9/24/2020.
//
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <math.h>
#include <errno.h>
#include <poll.h>
#include <jni.h>
#include "PollFileService.h"
int PollFileService::iPollStatus = 0;
int PollFileService::iPollRet = 0;
int PollFileService::iPollRevents = 0;
PollFileService::PollFileService(const char *pathName /* = nullptr */, int timeMilliSec /* = -1 */) : iValue(23), fd(-1)
{
iPollStatus = 0;
if (pathName) {
fd = open (pathName, O_RDONLY);
}
}
PollFileService::~PollFileService()
{
if (fd >= 0) {
close (fd);
fd = -1;
}
}
int PollFileService::PollFileCheck(const char *pathName, int timeMilliSec /* = -1 */)
{
struct pollfd fdList[] = {
{fd, POLLPRI | POLLERR, 0},
{0}
};
nfds_t nfds = 1;
unsigned char tempbuff[256] = {0};
if (fd < 0 && pathName) {
fd = open (pathName, O_RDONLY);
fdList[0].fd = fd;
}
// with a edge triggered GPIO that we are going to use the poll(2)
// function to wait on an event, we need to read from the
// pin before we do the poll(2). If the read is not done then
// the poll(2) returns with both POLLPRI and POLLERR set in the
// revents member. however if we read first then do the poll2()
// the poll(2) will wait for the event, input voltage change with
// either a rising edge or a falling edge, depending on the setting
// in the /edge pseudo file.
ssize_t iCount = read (fdList[0].fd, tempbuff, 255);
iPollStatus = PollErrorUNKNOWN;
int iRet = poll(fdList, nfds, timeMilliSec);
if (iRet == 0) {
iPollStatus = PollTimeOut;
} else if (iRet < 0) {
switch (errno) {
case EFAULT:
iPollStatus = PollErrorEFAULT;
break;
case EINTR:
iPollStatus = PollErrorEINTR;
break;
case EINVAL:
iPollStatus = PollErrorEINVAL;
break;
case ENOMEM:
iPollStatus = PollErrorENOMEM;
break;
default:
iPollStatus = PollErrorUNKNOWN;
break;
}
} else if (iRet > 0) {
// successful call now determine what we should return.
iPollRevents = fdList[0].revents; /* & (POLLIN | POLLPRI | POLLERR); */
switch (fdList[0].revents & (POLLIN | POLLPRI | POLLERR /* | POLLNVAL | POLLHUP*/)) {
case (POLLIN): // value of 1, There is data to read.
case (POLLPRI): // value of 2, There is urgent data to read
case (POLLOUT): // , Writing now will not block.
case (POLLIN | POLLPRI): // value of 3
iPollStatus = PollSuccess;
break;
// testing with a DragonBoard 410C indicates that we may
// see the POLLERR indicator set in revents along with
// the POLLIN and/or POLLPRI indicator set indicating there
// is data to be read.
// see as well poll(2) man page which states:
// POLLERR Error condition (output only).
case (POLLIN | POLLERR): // value of 9
case (POLLPRI | POLLERR): // value of 10
case (POLLIN | POLLPRI | POLLERR): // value of 11
iPollStatus = PollSuccess;
break;
case (POLLHUP): // , Hang up (output only).
iPollStatus = PollErrorPOLLHUP;
break;
case (POLLERR): // value of 8, Error condition (output only).
iPollStatus = PollErrorPOLLERR;
break;
case (POLLNVAL): // , Invalid request: fd not open (output only).
iPollStatus = PollErrorPOLLNVAL;
break;
case (POLLERR | POLLNVAL):
iPollStatus = PollErrorPOLLERRNVAL;
break;
default:
iPollStatus = PollErrorPOLLERRDEFLT;
break;
}
}
return iPollStatus;
}
int PollFileService::PollFileRead (const char *pathName /* = nullptr */)
{
char buffer[12] = {0};
int iRet = -1;
if (fd < 0 && pathName) {
fd = open (pathName, O_RDONLY);
}
int nCount = read (fd, buffer, 10);
if (nCount > 0) {
iRet = atoi (buffer);
}
return iRet;
}
// Check the specified file using the poll(2) service and
// return a status as follows:
// - 0 -> poll(2) success indicating something is available
// - 1 -> poll(2) failed with time out before anything available
// - -1 -> poll(2) error - EFAULT
// - -2 -> poll(2) error - EINTR
// - -3 -> poll(2) error - EINVAL
// - -4 -> poll(2) error - ENOMEM
// - -5 -> poll(2) error - POLLERR
// - -6 -> poll(2) error - POLLNVAL
// - -7 -> poll(2) error - POLLERR | POLLNVAL
// - -8 -> poll(2) error - POLLHUP
// - -9 -> poll(2) error - poll(2) revent indicator Unknown
// - -100 -> poll(2) error - Unknown error
//
static int lastRevents = 0;
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_myapplication_MainActivity_pollFileWithTimeOut (JNIEnv* pEnv, jobject pThis, jstring pKey, jint timeMS)
{
char *pathName;
int timeMilliSec;
PollFileService myPoll;
const char *str = pEnv->GetStringUTFChars(pKey, 0);
int timeMSint = 10000; // timeMS;
#if 1
int iStatus = myPoll.PollFileCheck(str, timeMSint);
#else
int iStatus = myPoll.PollFileRead(str);
#endif
pEnv->ReleaseStringUTFChars(pKey, str);
lastRevents = myPoll.iPollRevents;
return iStatus;
}
#if 0
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_myapplication_MainActivity_pollGetLastStatus (JNIEnv* pEnv, jobject pThis) {
return PollFileService::iPollStatus;
}
#endif
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_myapplication_MainActivity_pollGetLastRevents (JNIEnv* pEnv, jobject pThis)
{
return lastRevents;
}