如何使用poll2或select2服务调用来查看伪文件与Kotlin的更改

如何解决如何使用poll2或select2服务调用来查看伪文件与Kotlin的更改

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

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

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

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

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

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

WatchService

https://www.kernel.org/doc/Documentation/gpio/sysfs.txt

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

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

“边缘” ...读作“无”,“上升”,“下降”或“两者”。 编写这些字符串以选择将使 返回“值”文件中的poll(2)。

仅当该引脚可以配置为中断时,此文件才存在 生成输入引脚。

附加说明

注意1:使用// 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() } 实用程序,我可以adb进入我的DragonBoard 410C,并测试了配置shell的物理引脚26 GPIO971的配置directionin设置为edge。在连接到物理引脚23 GPIO938的面包板上使用简单的LED电路,并添加一条从物理引脚26到物理引脚23管理的LED的电线,我能够使用rising和然后echo 1 > gpio938/value看到物理引脚26的值变高并且读为cat gpio971/value。然后,我用1断开了连接到物理引脚23的LED,然后echo 0 > gpio938/value如期返回了cat gpio971/value的值。

但是,这个实验并没有告诉我0是否会指示打开和关闭LED时poll(2)上的更改。

注释1a::我有一个本机C ++ JNI函数的第一版来实现gpio971/value服务调用,并且已经在我的DragonBoard 410C上进行了测试。我看到的是poll(2)函数将立即返回,并在poll(2)数组的POLLIN成员中同时设置POLLERRrevents

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

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

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

注释2:我试图在我的Kotlin应用程序中使用poll(2),但遇到一个错误,提示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之后,有人提出了可行的解决方案,可以在启动poll(2)之前读取伪文件。

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

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

但是测试表明,当使用1.8v电源的电线手动施加电压时,通过超时或返回pollPseudoFile()中的值,函数/value可以正常工作(引脚38)连接到GPIO输入引脚,该引脚通过rising伪文件中的falling/edge设置。

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

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

// 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()服务调用。

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按钮单击侦听器,如下所示:

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-