0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何在Android 10设备上通过App控制GPIO

2132110287 来源:万象奥科 2024-07-22 17:59 次阅读

本文档提供了在 Android 10 设备上通过应用程序(App)控制通用输入输出(GPIO)的详细指南。这涵盖了从创建 gpio驱动到App 配置 以及 SELinux 策略以允许特定访问的所有必要步骤。

1.1驱动实现

添加创建gpio控制驱动bspkernelkernel4.14driversgpiogpio_led.c,并添加好对应的Makfile编译

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include  
#include
#include
#include
#include
#include
#include
#include
#include
#include


#define GPIO_HIGH _IO('L', 0)
#define GPIO_LOW _IO('L', 1)


#define LED_ON 1
#define LED_OFF 0
#define SIMPIE_LED_MAX 4


//============================== Upper interface value ==============================//
// 驱动模块名称定义
#define MODULE_NAME "gpio_led"     // 驱动模块的名字
#define MISC_NAME "gpio_led_device"   // 用于注册为“misc”设备的名字


// 模块函数接口定义,供上层应用调用的接口。通过MM_DEV_MAGIC区分不同系统接口,通过_IO()加上自己的编号作为接口number。
#define MM_DEV_MAGIC 'N'


// LED 控制命令
#define RFID_IO1 _IO(MM_DEV_MAGIC, 93)
#define RFID_IO2 _IO(MM_DEV_MAGIC, 130)
#define RFID_IO3 _IO(MM_DEV_MAGIC, 121)
#define RFID_LED _IO(MM_DEV_MAGIC, 138)


static int major;
static struct class *cls;


// GPIO 描述数组
struct gpio_desc *led_gpio[SIMPIE_LED_MAX];


// cat命令将调用该函数
static ssize_t gpio_value_show(struct device *dev, struct device_attribute *attr, char *buf)
{
  return sprintf(buf, "%d
", gpiod_get_value(led_gpio[0]));
}


// echo命令将调用该函数
static ssize_t gpio_value_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len)
{
  pr_err("[vanxoak]%c
", buf[0]);
  if ('0' == buf[0])
  {
    gpiod_direction_output(led_gpio[0], 0);
    pr_err("[vanxoak]: _%s_ :gpio off
", __func__);
  }
  else if ('1' == buf[0])
  {
    gpiod_direction_output(led_gpio[0], 1);
    pr_err("[vanxoak]: _%s_ :gpio on
", __func__);
  }
  else
    pr_err("I only support 0 or 1 to ctrl gpio on or off
");
  pr_err("[vanxoak]gpio_value_store
");
  return len;
}


// 定义一个名为gpio_led的设备属性
static DEVICE_ATTR(gpio_led, 0664, gpio_value_show, gpio_value_store);


// 提供给上层控制的接口
long gpio_led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
  switch (cmd)
  {
  case RFID_LED:
    gpiod_direction_output(led_gpio[0], arg);
    break;
  case RFID_IO1:
    gpiod_direction_output(led_gpio[1], arg);
    break;
  case RFID_IO2:
    gpiod_direction_output(led_gpio[2], arg);
    break;
  case RFID_IO3:
    gpiod_direction_output(led_gpio[3], arg);
    break;


  default:
    pr_err("[vanxoak] %s default: break
", __func__);
    break;  
  }
  return 0;
}


struct file_operations gpio_led_ops = {
  .owner = THIS_MODULE,
  .unlocked_ioctl = gpio_led_ioctl,
};


// LED灯初始化
static int simpie_led_init(struct platform_device *pdev)
{
  int ret = 0;
  int i;


  // 申请gpio设备
  led_gpio[0] = devm_gpiod_get(&pdev->dev, "led0", GPIOD_OUT_LOW);
  led_gpio[1] = devm_gpiod_get(&pdev->dev, "led1", GPIOD_OUT_LOW);
  led_gpio[2] = devm_gpiod_get(&pdev->dev, "led2", GPIOD_OUT_LOW);
  led_gpio[3] = devm_gpiod_get(&pdev->dev, "led3", GPIOD_OUT_LOW);


  for (i = 0; i < SIMPIE_LED_MAX; i++)
    {
        if (IS_ERR(led_gpio[i]))
        {
            ret = PTR_ERR(led_gpio[i]);
            return ret;
        }
        // 输出初始电平
        ret = gpiod_direction_output(led_gpio[i], LED_OFF);
    }


    device_create_file(&pdev->dev, &dev_attr_gpio_led);
  return ret;
}


// 驱动入口
static int gpio_led_probe(struct platform_device *pdev)
{
  int ret = 0;
  pr_err("[vanxoak]gpio_led_probe start...
");


  // LED灯gpio初始化及输出配置
  ret = simpie_led_init(pdev);  


  pr_err("[vanxoak]gpio_led_probe end...
");


  return 0;
}


// 绑定设备
static struct of_device_id gpio_led_match_table[] = {
  {.compatible = "yz,gpio-led"},
  {}};


static int gpio_led_remove(struct platform_device *pdev)
{
  pr_err("[vanxoak]gpio_led_remove...
");
  return 0;
}


static struct platform_driver gpio_led_driver = {
  .driver = {
    .name = MODULE_NAME,
    .owner = THIS_MODULE,
    .of_match_table = gpio_led_match_table,
  },
  .probe = gpio_led_probe,
  .remove = gpio_led_remove,
};


// gpio初始化入口
static int gpio_led_init(void)
{
  struct device *mydev;


  pr_err("[vanxoak]gpio_led_init start...
");
  platform_driver_register(&gpio_led_driver);


  major = register_chrdev(0, "gpiotest", &gpio_led_ops);


  // 创建gpio_led_class设备
  cls = class_create(THIS_MODULE, "gpio_led_class");


  // 在gpio_led_class设备目录下创建一个gpio_led_device属性文件
  mydev = device_create(cls, 0, MKDEV(major, 0), NULL, MISC_NAME);
  if (sysfs_create_file(&(mydev->kobj), &dev_attr_gpio_led.attr))
  {  
    return -1;
  }


  return 0;
}


static void gpio_led_exit(void)
{
  pr_err("[vanxoak]gpio_led_exit...
");
  platform_driver_unregister(&gpio_led_driver);


  device_destroy(cls, MKDEV(major, 0));
  class_destroy(cls);
  unregister_chrdev(major, "gpiotest");
}


module_init(gpio_led_init);
module_exit(gpio_led_exit);


MODULE_DESCRIPTION("Device_create Driver");
MODULE_LICENSE("GPL");


设备树配置 
gpio_led: yz,gpio-led {
        status = "disabled";
        compatible = "yz,gpio-led";
        led0-gpio = <&ap_gpio 138 GPIO_ACTIVE_HIGH>;
        led1-gpio = <&ap_gpio 93 GPIO_ACTIVE_HIGH>;
        led2-gpio = <&ap_gpio 130 GPIO_ACTIVE_HIGH>;
        led3-gpio = <&ap_gpio 121 GPIO_ACTIVE_HIGH>;
};

配置好上面gpio驱动后重新编译更新kernel 可以在/dev目录下找到对应的设备文件

"/dev/gpio_led_device",通过读写设备文件就可以操作gpio了。

1.2创建 Native 库

1.2.1设置 JNI 方法

在 App 中定义 JNI 方法以实现与 GPIO 设备的交互。

public class NativeClass {     






  static {     
    try {     
      System.loadLibrary("jni_gpiocontrol");     
      Log.d("NativeClass", "Native library loaded successfully.");     
    } catch (UnsatisfiedLinkError e) {     
      Log.e("NativeClass", "Failed to load native library: " + e.getMessage());     
      // throw new RuntimeException("Failed to load native library", e);     
    }     
  }     
  // 声明本地方法     
  public native int controlGPIO(int cmd, long arg);     
}

1.2.2实现 Native 方法

在app/src/main目录下创建一个cpp文件夹(如果你的项目是用Kotlin编写的,这个步骤仍然适用,因为JNI是用C/C++实现的)。将你的libjni_gpiocontrol.cpp文件放到这个cpp目录中。

注意事项:确保本地方法签名正确,Java方法签名和本地(C/C++)方法实现之间必须完全匹配。

#include
#include
#include
#include
#include
#include
#include
#include


#define MM_DEV_MAGIC 'N'
#define RFID_LED _IO(MM_DEV_MAGIC, 138)
#define RFID_IO1 _IO(MM_DEV_MAGIC, 93)
#define RFID_IO2 _IO(MM_DEV_MAGIC, 130)
#define RFID_IO3 _IO(MM_DEV_MAGIC, 121)  


#define DEVICE_PATH "/dev/gpio_led_device"
#define LOG_TAG "GPIOControl"




extern "C" JNIEXPORT jint JNICALL
Java_com_example_gpio_NativeClass_controlGPIO(JNIEnv *env, jobject obj, jint cmd, jlong arg) {


  int device_fd;
  long ioctl_result;
  unsigned int ioctl_cmd = cmd;


  // Open the device file
  device_fd = open(DEVICE_PATH, O_RDWR);
  if (device_fd < 0) {
        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Could not open device: %s", strerror(errno));
        return -1;
    }


    // Translate cmd to appropriate ioctl command based on input
    switch (cmd) {
        case 138:
            ioctl_cmd = RFID_LED;
            break;
        case 93:
            ioctl_cmd = RFID_IO1;
            break;
        case 130:
            ioctl_cmd = RFID_IO2;
            break;
        case 121:
            ioctl_cmd = RFID_IO3;
            break;
        default:
            __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Invalid command");
            close(device_fd);
            return -1;
    }


    // Send an ioctl to the device
    ioctl_result = ioctl(device_fd, ioctl_cmd, arg);
    if (ioctl_result < 0) {
        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Failed to call ioctl: %s", strerror(errno));    
        close(device_fd);
        return -1;
    }


    // Close the device
    close(device_fd);


    return 0;
}

1.2.3编译 Native 库

使用 CMake 或 ndk-build 工具编译你的 native 代码为共享库(.so 文件)。

添加appsrcmaincppCMakeLists.txt

cmake_minimum_required(VERSION 3.4.1)
project("gpiotest")
add_library(jni_gpiocontrol SHARED libjni_gpiocontrol.cpp)




find_library( log-lib log )


target_link_libraries(jni_gpiocontrol
            ${log-lib} ) 

1.2.4调用 Native 方法

通过 JNI 接口在 App 中调用实现的 native 方法以控制 GPIO。

public class MainActivity extends AppCompatActivity {
  private NativeClass nativeClass;


  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);


    nativeClass = new NativeClass();


    // 示例:打开LED  
    int result = nativeClass.controlGPIO(138, 1);
    // 根据result处理结果
  }
}

2.SELinux 配置

由于直接访问硬件设备在 Android 中受到 SELinux 策略的限制,需要修改 SELinux 策略以允许 App 访问 GPIO 设备文件。

定义设备类型:为 GPIO 设备定义一个新的 SELinux 类型(如 gpio_led_device_t)。

SDK_dir/device/sprd/sharkle/common/sepolicy/device.te 添加

# 定义新的设备类型
type gpio_led_device_t, dev_type;

分配文件上下文:为 GPIO 设备文件分配新定义的 SELinux 类型。

SDK_dir/device/sprd/sharkle/common/sepolicy/file_contexts中添加

/dev/gpio_led_device ugpio_led_device_t:s0

授予权限:在 SELinux 策略中添加规则,允许 App 访问 GPIO 设备。

SDK_dir/device/sprd/sharkle/common/sepolicy/system_app.te

# 允许 system_app 访问 gpio_led_device
allow system_app gpio_led_device_t:chr_file { read write };

重新编译 SELinux 策略:对更改的 SELinux 策略进行编译,并将其部署到设备上。这一步骤的目的是将自定义的安全策略更改应用到Android构建系统的预设SELinux策略中,确保在编译系统镜像时,这些更改会被包含进去。

cp system/sepolicy/public/app.te system/sepolicy/prebuilts/api/29.0/public/app.te
cp system/sepolicy/private/coredomain.te system/sepolicy/prebuilts/api/29.0/private/coredomain.te

3.测试与部署

测试 App:在具有所需硬件支持的 Android 10 设备上测试 App。确保 App 能成功加载 native 库,并能通过 JNI 调用控制 GPIO。

SELinux 策略测试:验证 SELinux 策略更改是否允许 App 无障碍地访问 GPIO 设备。

问题排查:如果遇到访问被拒绝的情况,请检查 SELinux 审计日志以确定是否需要进一步调整策略。

3.1注意事项

安全性:在修改 SELinux 策略以增加访问权限时,务必小心谨慎,避免引入安全漏洞。

设备兼容性:确保你的实现考虑到了不同设备可能存在的硬件和配置差异。

文档和维护:适当记录你的设计和实现过程,包括 JNI 接口、native 代码和 SELinux 策略更改,以便于未来的审计和维护。

通过遵循以上步骤,你可以在遵守 Android 安全模型的同时,实现 App 对 GPIO 的有效控制。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • APP
    APP
    +关注

    关注

    33

    文章

    1553

    浏览量

    72130
  • 编译
    +关注

    关注

    0

    文章

    645

    浏览量

    32646
  • GPIO
    +关注

    关注

    16

    文章

    1166

    浏览量

    51444

原文标题:如何以JNI方式实现安卓APP控制GPIO?

文章出处:【微信号:万象奥科,微信公众号:万象奥科】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Android移动设备应用机器学习

    我们考虑了如何在 Android 操作系统中使用预训练的图像分类模型。此外,模型的使用也可以在嵌入式设备使用,因为 TensorFlow Lite 解释器还具有 C++ 接口并占用约
    发表于 07-09 07:20 1214次阅读

    CC2650读取Android app显示的服务问题

    CC2650读取Android app显示的服务问题这个是CC2650板子跑HeartRate时,APP显示的服务,并没有 HEARTR
    发表于 04-07 11:09

    如何使用核心模块开发设备控制功能(iOS APP开发)

    Android Demo 示例源码,包含了设备网络配置、用户注册登录、设备状态查询、设备远程控制等功能的参考实现,开发者可
    发表于 11-29 18:18

    牛掰了!鸿蒙与Android完美融合,将鸿蒙设备Android设备

    并不能分辨哪一个是Android App,哪一个是HarmonyOS App,所以HarmonyOS真正做到了两套系统无缝切换,用户完全感觉不到。3.用adb命令行控制HarmonyO
    发表于 11-13 09:44

    如何通过APP控制串口屏和GPIO口的电平

    简介:本文将通过介绍如何实现屏和App实时显示温湿度和光照度采集数据,以及通过控制屏和App控制
    发表于 02-21 06:51

    如何通过kernel设备文件节点打通android app与kernel间的通信?

    如何通过kernel设备文件节点打通android app与kernel间的通信?
    发表于 03-04 06:10

    如何通过kernel设备文件节点打通android app与kernel间的通信?

    如何通过kernel设备文件节点打通android app与kernel间的通信?
    发表于 03-04 06:40

    请问如何在Android 9以上的设备正常使用这个功能?

    请问如何在Android 9以上的设备正常使用这个功能?好像是https或者http引起的。 谢谢
    发表于 05-25 08:30

    Android设备通过蓝牙模块实现无线终端的设计

    的RISC 内核的ATMEGA 8 为核心,并集成蓝牙串行模块为通信信道。Android 设备通过蓝牙将数据发送至下位机,下位机根据接收到数据,完成相应的控制功能,并将处理结果上传,实
    的头像 发表于 07-11 08:10 3178次阅读
    <b class='flag-5'>Android</b><b class='flag-5'>设备</b><b class='flag-5'>通过</b>蓝牙模块实现无线终端的设计

    何在嵌入式Linux中使用GPIO

    了解如何在嵌入式Linux中使用GPIO,特别强调Zynq-7000系列。 我们介绍了基本的用户和内核空间GPIO使用情况,以及GPIOGPIO
    的头像 发表于 11-26 07:02 3975次阅读

    未来Windows 10将会原生运行Android App?

    11月30日早间消息,据外媒Windows Central 报道,微软内部正在开发内部代号为“Latte”的新项目,这个项目将允许应用程序开发人员将他们Android 应用程序带到Windows 10,也就是说未来Windows 10
    的头像 发表于 11-30 10:28 1431次阅读

    Android: 使用Linux regulator系统通过一个GPIO控制外部IC的电源

    Android: 使用Linux regulator系统通过一个GPIO控制外部IC的电源一、通过一个G
    发表于 01-06 16:41 0次下载
    <b class='flag-5'>Android</b>: 使用Linux regulator系统<b class='flag-5'>通过</b>一个<b class='flag-5'>GPIO</b><b class='flag-5'>控制</b>外部IC的电源

    何在STM8S控制执行通用GPIO功能实现LED闪烁

    在本教程中,我们将学习如何在 STM8S 控制执行通用 GPIO 功能。该板已经有一个连接到端口 B 的引脚 5 的板载 LED,我们将学习如何使该 LED 闪烁,并添加一个外部
    的头像 发表于 08-11 17:04 5100次阅读
    如<b class='flag-5'>何在</b>STM8S<b class='flag-5'>控制</b>器<b class='flag-5'>上</b>执行通用<b class='flag-5'>GPIO</b>功能实现LED闪烁

    Arduino/Android蓝牙伺服电机控制安卓APP

    电子发烧友网站提供《Arduino/Android蓝牙伺服电机控制安卓APP.zip》资料免费下载
    发表于 12-05 09:14 4次下载
    Arduino/<b class='flag-5'>Android</b>蓝牙伺服电机<b class='flag-5'>控制</b>安卓<b class='flag-5'>APP</b>

    通过WiFi使用Android设备控制机器人汽车

    电子发烧友网站提供《通过WiFi使用Android设备控制机器人汽车.zip》资料免费下载
    发表于 01-03 14:02 0次下载
    <b class='flag-5'>通过</b>WiFi使用<b class='flag-5'>Android</b><b class='flag-5'>设备</b><b class='flag-5'>控制</b>机器人汽车