需求描述
上层APK操作GPIO的diretction、value节点,实现点亮和熄灭灯,使用JNI方式,使用以下方式无效
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_administrator_jni_Gpio_setOpenLed(JNIEnv *env, jclass instance) {
int sd;
system("echo 9 > /sys/class/gpio/export");
system("echo out > /sys/class/gpio/gpio9/direction");
system("echo 1 > /sys/class/gpio/gpio9/value");
system("echo 9 > /sys/class/gpio/unexport");
return 0;
}
实现方案
- GPIOControl.c:JNI调用接口
#include <jni.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <android/log.h>
#define TAG "jni_gpio"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__)
#define IN 0
#define OUT 1
#define LOW 0
#define HIGH 1
#define BUFFER_MAX 3
#define DIRECTION_MAX 48
/*
* Class: com_example_jnigpio_GPIOControl
* Method: exportGpio
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_com_example_jnigpio_GPIOControl_exportGpio(JNIEnv *env, jobject instance, jint gpio)
{
char buffer[BUFFER_MAX];
int len;
int fd;
fd = open("/sys/class/gpio/export", O_WRONLY);
if (fd < 0) {
LOGE("Failed to open export for writing!\n");
return(0);
}
len = snprintf(buffer, BUFFER_MAX, "%d", gpio);
if (write(fd, buffer, len) < 0) {
LOGE("Fail to export gpio!\n");
return 0;
}
close(fd);
return 1;
}
/*
* Class: com_example_jnigpio_GPIOControl
* Method: setGpioDirection
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_jnigpio_GPIOControl_setGpioDirection(JNIEnv *env, jobject instance, jint gpio, jint direction)
{
static const char dir_str[] = "in\0out";
char path[DIRECTION_MAX];
int fd;
snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/direction", gpio);
fd = open(path, O_WRONLY);
if (fd < 0) {
LOGE("failed to open gpio direction for writing!\n");
return 0;
}
if (write(fd, &dir_str[direction == IN ? 0 : 3], direction == IN ? 2 : 3) < 0) {
LOGE("failed to set direction!\n");
return 0;
}
close(fd);
return 1;
}
/*
* Class: com_example_jnigpio_GPIOControl
* Method: readGpioStatus
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_com_example_jnigpio_GPIOControl_readGpioStatus(JNIEnv *env, jobject instance, jint gpio)
{
char path[DIRECTION_MAX];
char value_str[3];
int fd;
snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/value", gpio);
fd = open(path, O_RDONLY);
if (fd < 0) {
LOGE("failed to open gpio value for reading!\n");
return -1;
}
if (read(fd, value_str, 3) < 0) {
LOGE("failed to read value!\n");
return -1;
}
close(fd);
return (atoi(value_str));
}
/*
* Class: com_example_jnigpio_GPIOControl
* Method: writeGpioStatus
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_jnigpio_GPIOControl_writeGpioStatus(JNIEnv *env, jobject instance, jint gpio, jint value)
{
static const char values_str[] = "01";
char path[DIRECTION_MAX];
int fd;
snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/value", gpio);
fd = open(path, O_WRONLY);
if (fd < 0) {
LOGE("failed to open gpio value for writing!\n");
return 0;
}
if (write(fd, &values_str[value == LOW ? 0 : 1], 1) < 0) {
LOGE("failed to write value!\n");
return 0;
}
close(fd);
return 1;
}
/*
* Class: com_example_jnigpio_GPIOControl
* Method: unexportGpio
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_com_example_jnigpio_GPIOControl_unexportGpio(JNIEnv *env, jobject instance, jint gpio)
{
char buffer[BUFFER_MAX];
int len;
int fd;
fd = open("/sys/class/gpio/unexport", O_WRONLY);
if (fd < 0) {
LOGE("Failed to open unexport for writing!\n");
return 0;
}
len = snprintf(buffer, BUFFER_MAX, "%d", gpio);
if (write(fd, buffer, len) < 0) {
LOGE("Fail to unexport gpio!");
return 0;
}
close(fd);
return 1;
}
- Android.mk:编译出
libGPIOControl.so
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := GPIOControl
LOCAL_SRC_FILES := GPIOControl.c
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
- 添加节点读写权限
device/qcom/common/rootdir/etc/init.qcom.rc
on boot
+ chmod 0777 /sys/class/gpio/export
+ chmod 0777 /sys/class/gpio/unexport
- 添加实际节点的读写权限:
ls -al sys/class/gpio/
查看实际节点
device/qcom/common/rootdir/etc/ueventd.qcom.rc
+/sys/devices/soc/1000000.pinctrl/gpio/gpio* direction 0777 system system
+/sys/devices/soc/1000000.pinctrl/gpio/gpio* value 0777 system system
- 添加节点的SELinux权限
device/qcom/sepolicy/common/system_app.te
+allow system_app sysfs:file rw_file_perms;
- APK调用
- Android应用修改AndroidManifest.xml文件,在manifest节点中添加
android:sharedUserId="android.uid.system
,修改Android.mk文件,添加LOCAL_CERTIFICATE := platform
设置系统签名。 - Android APK调用JNI加载so动态库:
https://blog.csdn.net/weixin_44008788/article/details/114393087
相关拓展
- 节点读写
//读节点
private static String readSys(String path) {
String prop = "waiting";// 默认值
try {
BufferedReader reader = new BufferedReader(new FileReader(path));
prop = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return prop;
}
//调用方法
readSys("/sys/class/demo/version")
//写节点
private static String writeSys(String path) {
try {
BufferedWriter bufWriter = null;
bufWriter = new BufferedWriter(new FileWriter(path));
bufWriter.write("1"); // 写操作
bufWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//调用方法
writeSys("/sys/class/demo/version")
//以FileOutputStream写节点
void IOCtrl(int pin, int level) {
String path;
path = "/sys/class/gpio/gpio" + pin + "/value";
try {
FileOutputStream out = new FileOutputStream(path);
out.write(level);
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
return;
}
}
- 添加设备文件节点权限
/sys/class/leds/green/brightness //快捷方式
/sys/devices/soc.0/gpio-leds.66/leds/green/brightness //实际节点
操作LED灯的设备文件节点为APP层system app进程开放该节点访问权限(读或写),权限配置主要修改/device/qcom/sepolicy/common目录下的file.te、file_contexts和system_app.te三个文件
file.te修改如下
# GPIO accessed by system app
type sysfs_gpio, fs_type, sysfs_type;
file_contexts修改如下
/sys/devices/soc/1010000.pinctrl/gpio/gpio62/value u:object_r:sysfs_gpio:s0
/sys/devices/soc/1010000.pinctrl/gpio/gpio63/value u:object_r:sysfs_gpio:s0
system_app.te修改如下
allow system_app sysfs_gpio:file rw_file_perms;
如果通过以上添加SELinux之后,仍没有权限读写sys或proc节点,需要到init.rc里面配置如下
chown system system 文件结点
chmod 777 文件结点
相关参考
https://blog.csdn.net/wds1181977/article/details/54967110
https://blog.csdn.net/liangtianmeng/article/details/84698568
https://blog.csdn.net/d38825/article/details/81240851
https://blog.csdn.net/K_Hello/article/details/90038078
android 查看gpio状态_Android应用层操作GPIO
Android app如何正确读写系统sys设备节点