如何使用poll(2)或select(2)服务调用来查看伪文件中Kotlin的更改 [英] how to use poll(2) or select(2) service call to watch a pseudo file for changes with Kotlin

查看:69
本文介绍了如何使用poll(2)或select(2)服务调用来查看伪文件中Kotlin的更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用使用Android 5.1和Kotlin的DragonBoard 410C来测试40针低功耗连接器上的GPIO针.我正在使用的库正在使用sysfs接口与GPIO引脚进行交互,这需要在/sys/class/gpio/目录树中打开各种伪文件并对这些文件读取和写入值,请参见

I am working with a DragonBoard 410C using Android 5.1 and Kotlin to experiment with the GPIO pins on the 40 pin low power connector. The library I'm using is using the sysfs interface for interacting with the GPIO pins which requires opening various pseudo files in /sys/class/gpio/ directory tree and reading and writing values to those files, see accessing GPIO low power connector on DragonBoard 410C running Android

我的理解是,我可以提供一个GPIO引脚作为输入和边沿触发,这将允许我用瞬时接触开关连接一个简单的电路,并能够检测何时按下该开关.

My understanding is that I can provision a GPIO pin as Input and Edge triggered which would allow me to wire up a simple circuit with a momentary contact switch and be able to detect when the switch is pressed.

但是我发现的文档表明我需要使用投票(2)系统服务或文件描述符的 select(2)系统服务我正在使用的GPIO引脚的/value伪文件,用于检测何时检测到Edge,例如/sys/class/gpio/gpio910/value.

However the documentation I've found indicates that I need to use the poll(2) system service or the select(2) system service on a file descriptor for the /value pseudo file of the GPIO pin I am using in order to detect when the Edge is detected, e.g. /sys/class/gpio/gpio910/value.

如何在Kotlin中将poll(2)select(2)系统服务与文件描述符一起使用? poll(2)FileReaderready()方法相同吗?

How do I use the poll(2) or select(2) system services with a file descriptor in Kotlin? Is poll(2) the same as ready() method of FileReader?

也许需要类似于Java WatchService功能的东西? http://www.java2s.com/教程/Java/java.nio.file/WatchService/0060__WatchService.poll_.htm

Perhaps something similar to the Java WatchService functionality is needed? http://www.java2s.com/Tutorials/Java/java.nio.file/WatchService/0060__WatchService.poll_.htm

除非是错误的方法,否则我计划的是具有实用程序功能,例如:

What I am planning, unless this is the wrong approach, is to have a utility function, something like:

// 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

值" ...读为0(低)或1(高).如果GPIO是 配置为输出,可以写入该值;任何非零 值被视为高.

"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.

如果该引脚可以配置为产生中断的中断,并且 如果已将其配置为生成中断(请参见 描述"edge"),则可以对该文件执行poll(2)和poll(2) 每当中断被触发时,它将返回.如果您使用 poll(2),设置事件POLLPRI和POLLERR.如果使用select(2), 在exceptfds中设置文件描述符.在poll(2)返回之后, lseek(2)到sysfs文件的开头并读取新值 或关闭文件,然后重新打开以读取值.

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.

边缘" ...读为无",上升",下降"或两者". 编写这些字符串以选择将使 轮询(2)上的值"文件返回.

"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设置为inedge设置为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返回了预期的0值.

Note 1: Using the adb utility I was able to shell into my DragonBoard 410C and tested configuring physical pin 26, GPIO971, with direction set to in and edge set to rising. Using a simple LED circuit on a breadboard that was tied to physical pin 23, GPIO938, and adding a wire from physical pin 26 to the LED managed by physical pin 23, I was able to turn on the LED with echo 1 > gpio938/value and then cat gpio971/value to see that the value for physical pin 26 had gone high and was reading as 1. I then turned the LED connected to physical pin 23 off with echo 0 > gpio938/value and then cat gpio971/value returned a value of 0 as expected.

但是,此实验并没有告诉我poll(2)是否会指示打开和关闭LED时gpio971/value的更改.

However this experiment does not tell me if a poll(2) would indicate a change on gpio971/value when the LED was turned on and turned off or not.

注释1a::我具有本机C ++ JNI函数的第一个版本,用于实现poll(2)服务调用,并且已经在我的DragonBoard 410C上进行了测试.我看到的是poll(2)函数立即返回,同时在struct pollfd数组的revents成员中设置了POLLINPOLLERR.

Note 1a: I have a first version of a Native C++ JNI function to implement the poll(2) service call and have been testing it with my DragonBoard 410C. What I am seeing is that the poll(2) function is immediately returning with both POLLIN and POLLERR set in the revents member of the struct pollfd array.

该测试使用的是连接到面包板行的物理引脚26,而LED的一条腿连接到物理引脚23,我可以打开和关闭它.当我尝试以10000毫秒的超时时间打开轮询时,无论两个指示灯均已设置,无论LED点亮(引脚26的值为1)还是不点亮(引脚26的值为0),呼叫都会立即返回.

The test is using physical pin 26 connected to a breadboard row with one leg of an LED connected to physical pin 23 which I am able to turn on and off. When I attempt to turn on polling with a 10000 ms time out, the call returns immediately whether the LED is lit (pin 26 value is 1) or not lit (pin 26 value is 0) with both indicators set.

我的期望是,因为我将edge设置为rising,所以只有在LED熄灭然后打开电源或经过10秒钟后,我才应该看到poll(2)返回.

My expectation is that since I have edge set to rising, I should see the poll(2) return only when the LED is unlit and I then turn it on or 10 seconds have elapsed.

在继续调查的过程中,我可能会发现我如何使用在应用程序的Kotlin端编写的Native C ++函数.

Am continuing my investigation as it strikes me there may a problem with how I'm using the Native C++ function I wrote in the Kotlin side of the app.

注意2:我尝试在我的Kotlin应用程序中使用WatchService,并遇到一个错误,该错误WatchService需要API级别26,而我在Android Studio中的最低目标是API级别22.似乎WatchService需要Android 8.0(Oreo),而DragonBoard是Android 5.1(Lollipop),因此无法使用WatchService监视文件状态.

Note 2: I attempted to use WatchService with my Kotlin application and ran into an error that WatchService required an API level 26 and my minimum target in Android Studio is API level 22. It looks like WatchService requires Android 8.0 (Oreo) while the DragonBoard is at Android 5.1 (Lollipop) so using WatchService to monitor file status is not available to me.

推荐答案

我采用的方法是创建本机C ++ JNI函数,以提供实现

The approach I am taking is to create a native C++ JNI function to provide a way to implement the poll(2) Linux service call.

我在开发和测试过程中遇到的一个有趣的问题是poll()立即返回,而不是等待超时或GPIO输入引脚的电压.在DragonBoard 410C的96Boards.org论坛上发布后,

One interesting issue I ran into during development and testing was the poll() returning immediately rather than waiting on either a time out or a voltage to the GPIO input pin. After posting on the 96Boards.org forum for the DragonBoard 410C, How to use poll() with sysfs interface to input GPIO pin to handle a switch press event, someone proposed a possible solution which worked, to read the pseudo file before starting the poll(2).

要使用此功能,我需要具有某种Kotlin协程或侧线程,以便在主UI处理按钮单击时开始对GPIO输入引脚的轮询,而主UI线程不是直到该函数因GPIO事件或超时返回而被阻止.

In order to use this function, I need to have some kind of a Kotlin coroutine or side thread so that when the main UI is processing a button click which starts the polling of the GPIO input pin, the main UI thread is not blocked until the function returns with either a GPIO event or a time out.

我还无法辨别如何制作这样的协程,因此这仍在进行中.经过一番思考,看来某种事件侦听器体系结构将是最合适的方法.

I have not yet been able to discern how to do such a coroutine so this is still a work in progress. After some thinking, it appears that some kind of an event listener architecture would be the most appropriate approach.

但是,通过使用1.8v电源(引脚38)到12v的导线手动施加电压时,通过超时或返回/value中的值,测试表明功能pollPseudoFile()正常工作.在/edge伪文件中用risingfalling设置设置的GPIO输入引脚.

However testing indicates that the function pollPseudoFile() is working properly by either doing a time out or returning with a value from /value when a voltage is applied by hand using a wire from the 1.8v power (pin 38) to the GPIO input pin that is set with either a rising or falling setting in the /edge pseudo file.

以下是Native C ++ JNI函数的源代码.我正在将其与以下Kotlin源代码一起使用.

The source code for the Native C++ JNI function is below. I am using it with the following Kotlin source code.

首先在我的MainActivity.kt源文件中,使Native C ++库可用于以下源:

First of all in my MainActivity.kt source file, I make the Native C++ library available with the following source:

// See the StackOverFlow question with answer at URL:
//    https://stackoverflow.com/questions/36932662/android-how-to-call-ndk-function-from-kotlin
init {
    System.loadLibrary("pollfileservice")
}

external fun pollFileWithTimeOut(pathPseudo : String, timeOutMs : Int): Int
external fun pollGetLastRevents() : Int

接下来,我在Kotlin源文件Gpio.kt中使用此功能来实际对伪文件执行poll()服务调用.

Next I'm using this function in the Kotlin source file Gpio.kt to actually perform the poll() service call on the pseudo file.

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类在实际的UI按钮单击侦听器中使用,如下所示:

The above Gpio class is used in the actual UI button click listener as follows:

            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

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

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

这篇关于如何使用poll(2)或select(2)服务调用来查看伪文件中Kotlin的更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆