当前位置:   article > 正文

如何以JNI方式实现安卓APP控制GPIO?_mtk安卓驱动jni之gpio驱动

mtk安卓驱动jni之gpio驱动

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

1. 驱动实现

添加创建gpio控制驱动bsp\kernel\kernel4.14\drivers\gpio\gpio_led.c,并添加好对应的Makfile编译

  1. #include <linux/init.h>
  2. #include <linux/slab.h>
  3. #include <linux/module.h>
  4. #include <linux/kernel.h>
  5. #include <linux/fs.h>
  6. #include <linux/cdev.h>
  7. #include <linux/device.h>
  8. #include <linux/ioctl.h>
  9. #include <linux/uaccess.h>
  10. #include <linux/string.h>
  11. #include <linux/wait.h>
  12. #include <linux/types.h>
  13. #include <linux/proc_fs.h>
  14. #include <linux/of.h>
  15. #include <linux/of_gpio.h>
  16. #include <linux/gpio.h>
  17. #include <linux/delay.h>
  18. #include <linux/platform_device.h>
  19. #include <linux/err.h>
  20. #include <linux/gpio/consumer.h>
  21. #include <linux/io.h>
  22. #include <linux/miscdevice.h>
  23. #include <linux/irq.h>
  24. #include <linux/of_irq.h>
  25. #include <linux/kernel.h>
  26. #include <linux/dmi.h>
  27. #include <linux/firmware.h>
  28. #include <linux/gpio/consumer.h>
  29. #include <linux/input.h>
  30. #include <linux/input/mt.h>
  31. #include <linux/module.h>
  32. #include <linux/delay.h>
  33. #include <linux/irq.h>
  34. #include <linux/interrupt.h>
  35. #include <linux/slab.h>
  36. #include <linux/acpi.h>
  37. #include <linux/of.h>
  38. #include <asm/unaligned.h>
  39. #define GPIO_HIGH _IO('L'0)
  40. #define GPIO_LOW _IO('L'1)
  41. #define LED_ON 1
  42. #define LED_OFF 0
  43. #define SIMPIE_LED_MAX 4
  44. //============================== Upper interface value ==============================//
  45. // 驱动模块名称定义
  46. #define MODULE_NAME "gpio_led"          // 驱动模块的名字
  47. #define MISC_NAME "gpio_led_device"     // 用于注册为“misc”设备的名字
  48. // 模块函数接口定义,供上层应用调用的接口。通过MM_DEV_MAGIC区分不同系统接口,通过_IO()加上自己的编号作为接口number
  49. #define MM_DEV_MAGIC 'N'
  50. // LED 控制命令
  51. #define RFID_IO1 _IO(MM_DEV_MAGIC, 93)
  52. #define RFID_IO2 _IO(MM_DEV_MAGIC, 130)
  53. #define RFID_IO3 _IO(MM_DEV_MAGIC, 121)
  54. #define RFID_LED _IO(MM_DEV_MAGIC, 138)
  55. static int major;
  56. static struct class *cls;
  57. // GPIO 描述数组
  58. struct gpio_desc *led_gpio[SIMPIE_LED_MAX];
  59. // cat命令将调用该函数
  60. static ssize_t gpio_value_show(struct device *dev, struct device_attribute *attr, char *buf)
  61. {
  62.     return sprintf(buf, "%d\n", gpiod_get_value(led_gpio[0]));
  63. }
  64. // echo命令将调用该函数
  65. static ssize_t gpio_value_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len)
  66. {
  67.     pr_err("[vanxoak]%c\n", buf[0]);
  68.     if ('0' == buf[0])
  69.     {
  70.         gpiod_direction_output(led_gpio[0], 0);
  71.         pr_err("[vanxoak]: _%s_ :gpio off\n", __func__);
  72.     }
  73.     else if ('1' == buf[0])
  74.     {
  75.         gpiod_direction_output(led_gpio[0], 1);
  76.         pr_err("[vanxoak]: _%s_ :gpio on\n", __func__);
  77.     }
  78.     else
  79.         pr_err("I only support 0 or 1 to ctrl gpio on or off\n");
  80.     pr_err("[vanxoak]gpio_value_store\n");
  81.     return len;
  82. }
  83. // 定义一个名为gpio_led的设备属性
  84. static DEVICE_ATTR(gpio_led, 0664, gpio_value_show, gpio_value_store);
  85. // 提供给上层控制的接口
  86. long gpio_led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  87. {
  88.     switch (cmd)
  89.     {
  90.     case RFID_LED:
  91.         gpiod_direction_output(led_gpio[0], arg);
  92.         break;
  93.     case RFID_IO1:
  94.         gpiod_direction_output(led_gpio[1], arg);
  95.         break;
  96.     case RFID_IO2:
  97.         gpiod_direction_output(led_gpio[2], arg);
  98.         break;
  99.     case RFID_IO3:
  100.         gpiod_direction_output(led_gpio[3], arg);
  101.         break;
  102.     default:
  103.         pr_err("[vanxoak] %s default: break\n", __func__);
  104.         break;
  105.     }
  106.     return 0;
  107. }
  108. struct file_operations gpio_led_ops = {
  109.     .owner = THIS_MODULE,
  110.     .unlocked_ioctl = gpio_led_ioctl,
  111. };
  112. // LED灯初始化
  113. static int simpie_led_init(struct platform_device *pdev)
  114. {
  115.     int ret = 0;
  116.     int i;
  117.     // 申请gpio设备
  118.     led_gpio[0= devm_gpiod_get(&pdev->dev, "led0", GPIOD_OUT_LOW);
  119.     led_gpio[1= devm_gpiod_get(&pdev->dev, "led1", GPIOD_OUT_LOW);
  120.     led_gpio[2= devm_gpiod_get(&pdev->dev, "led2", GPIOD_OUT_LOW);
  121.     led_gpio[3= devm_gpiod_get(&pdev->dev, "led3", GPIOD_OUT_LOW);
  122.     for (i = 0; i < SIMPIE_LED_MAX; i++)
  123.     {
  124.         if (IS_ERR(led_gpio[i]))
  125.         {
  126.             ret = PTR_ERR(led_gpio[i]);
  127.             return ret;
  128.         }
  129.         // 输出初始电平
  130.         ret = gpiod_direction_output(led_gpio[i], LED_OFF);
  131.     }
  132.     device_create_file(&pdev->dev, &dev_attr_gpio_led);
  133.     return ret;
  134. }
  135. // 驱动入口
  136. static int gpio_led_probe(struct platform_device *pdev)
  137. {
  138.     int ret = 0;
  139.     pr_err("[vanxoak]gpio_led_probe start...\n");
  140.     // LED灯gpio初始化及输出配置
  141.     ret = simpie_led_init(pdev);
  142.     pr_err("[vanxoak]gpio_led_probe end...\n");
  143.     return 0;
  144. }
  145. // 绑定设备
  146. static struct of_device_id gpio_led_match_table[] = {
  147.     {.compatible = "yz,gpio-led"},
  148.     {}};
  149. static int gpio_led_remove(struct platform_device *pdev)
  150. {
  151.     pr_err("[vanxoak]gpio_led_remove...\n");
  152.     return 0;
  153. }
  154. static struct platform_driver gpio_led_driver = {
  155.     .driver = {
  156.         .name = MODULE_NAME,
  157.         .owner = THIS_MODULE,
  158.         .of_match_table = gpio_led_match_table,
  159.     },
  160.     .probe = gpio_led_probe,
  161.     .remove = gpio_led_remove,
  162. };
  163. // gpio初始化入口
  164. static int gpio_led_init(void)
  165. {
  166.     struct device *mydev;
  167.     pr_err("[vanxoak]gpio_led_init start...\n");
  168.     platform_driver_register(&gpio_led_driver);
  169.     major = register_chrdev(0"gpiotest"&gpio_led_ops);
  170.     // 创建gpio_led_class设备
  171.     cls = class_create(THIS_MODULE, "gpio_led_class");
  172.     // 在gpio_led_class设备目录下创建一个gpio_led_device属性文件
  173.     mydev = device_create(cls, 0, MKDEV(major, 0), NULL, MISC_NAME);
  174.     if (sysfs_create_file(&(mydev->kobj), &dev_attr_gpio_led.attr))
  175.     {
  176.         return -1;
  177.     }
  178.     return 0;
  179. }
  180. static void gpio_led_exit(void)
  181. {
  182.     pr_err("[vanxoak]gpio_led_exit...\n");
  183.     platform_driver_unregister(&gpio_led_driver);
  184.     device_destroy(cls, MKDEV(major, 0));
  185.     class_destroy(cls);
  186.     unregister_chrdev(major, "gpiotest");
  187. }
  188. module_init(gpio_led_init);
  189. module_exit(gpio_led_exit);
  190. MODULE_DESCRIPTION("Device_create Driver");
  191. MODULE_LICENSE("GPL");

设备树配置 

  1. gpio_led: yz,gpio-led {
  2. status = "disabled";
  3. compatible = "yz,gpio-led";
  4. led0-gpio = <&ap_gpio 138 GPIO_ACTIVE_HIGH>;
  5. led1-gpio = <&ap_gpio 93 GPIO_ACTIVE_HIGH>;
  6. led2-gpio = <&ap_gpio 130 GPIO_ACTIVE_HIGH>;
  7. led3-gpio = <&ap_gpio 121 GPIO_ACTIVE_HIGH>;
  8. };

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

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

1.2 创建 Native 库

1.2.1设置 JNI 方法

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

  1. public class NativeClass {
  2.     static {
  3.         try {
  4.             System.loadLibrary("jni_gpiocontrol");
  5.             Log.d("NativeClass""Native library loaded successfully.");
  6.         } catch (UnsatisfiedLinkError e) {
  7.             Log.e("NativeClass""Failed to load native library: " + e.getMessage());
  8.             // throw new RuntimeException("Failed to load native library", e);
  9.         }
  10.     }
  11.     // 声明本地方法
  12.     public native int controlGPIO(int cmd, long arg);
  13. }
1.2.2 实现 Native 方法

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

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

  1. #include <jni.h>
  2. #include <fcntl.h>
  3. #include <unistd.h>
  4. #include <sys/ioctl.h>
  5. #include <stdio.h>
  6. #include <android/log.h>
  7. #include <errno.h>
  8. #include <string.h>
  9. #define MM_DEV_MAGIC 'N'
  10. #define RFID_LED _IO(MM_DEV_MAGIC, 138)
  11. #define RFID_IO1 _IO(MM_DEV_MAGIC, 93)
  12. #define RFID_IO2 _IO(MM_DEV_MAGIC, 130)
  13. #define RFID_IO3 _IO(MM_DEV_MAGIC, 121)
  14. #define DEVICE_PATH "/dev/gpio_led_device"
  15. #define LOG_TAG "GPIOControl"
  16. extern "C" JNIEXPORT jint JNICALL
  17. Java_com_example_gpio_NativeClass_controlGPIO(JNIEnv *env, jobject obj, jint cmd, jlong arg) {
  18.     int device_fd;
  19.     long ioctl_result;
  20.     unsigned int ioctl_cmd = cmd;
  21.     // Open the device file
  22.     device_fd = open(DEVICE_PATH, O_RDWR);
  23.     if (device_fd < 0) {
  24.         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Could not open device: %s", strerror(errno));
  25.         return -1;
  26.     }
  27.     // Translate cmd to appropriate ioctl command based on input
  28.     switch (cmd) {
  29.         case 138:
  30.             ioctl_cmd = RFID_LED;
  31.             break;
  32.         case 93:
  33.             ioctl_cmd = RFID_IO1;
  34.             break;
  35.         case 130:
  36.             ioctl_cmd = RFID_IO2;
  37.             break;
  38.         case 121:
  39.             ioctl_cmd = RFID_IO3;
  40.             break;
  41.         default:
  42.             __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Invalid command");
  43.             close(device_fd);
  44.             return -1;
  45.     }
  46.     // Send an ioctl to the device
  47.     ioctl_result = ioctl(device_fd, ioctl_cmd, arg);
  48.     if (ioctl_result < 0) {
  49.         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Failed to call ioctl: %s", strerror(errno));
  50.         close(device_fd);
  51.         return -1;
  52.     }
  53.     // Close the device
  54.     close(device_fd);
  55.     return 0;
  56. }

1.2.3 编译 Native 库

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

添加app\src\main\cpp\CMakeLists.txt 

  1. cmake_minimum_required(VERSION 3.4.1)
  2. project("gpiotest")
  3. add_library(jni_gpiocontrol SHARED libjni_gpiocontrol.cpp)
  4. find_library( log-lib log )
  5. target_link_libraries(jni_gpiocontrol
  6.                        ${log-lib} ) 
1.2.4 调用 Native 方法

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

  1. public class MainActivity extends AppCompatActivity {
  2.     private NativeClass nativeClass;
  3.     @Override
  4.     protected void onCreate(Bundle savedInstanceState) {
  5.         super.onCreate(savedInstanceState);
  6.         setContentView(R.layout.activity_main);
  7.         nativeClass = new NativeClass();
  8.         // 示例:打开LED
  9.         int result = nativeClass.controlGPIO(1381);
  10.         // 根据result处理结果
  11.     }
  12. }

2. SELinux 配置

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

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

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

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

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

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

/dev/gpio_led_device u:object_r:gpio_led_device_t:s0

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

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

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

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

  1. cp system/sepolicy/public/app.te system/sepolicy/prebuilts/api/29.0/public/app.te
  2. 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 的有效控制。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/751658
推荐阅读
相关标签
  

闽ICP备14008679号