本文檔提供了在 Android 10 設(shè)備上通過應(yīng)用程序(App)控制通用輸入輸出(GPIO)的詳細指南。這涵蓋了從創(chuàng)建 gpio驅(qū)動到App 配置 以及 SELinux 策略以允許特定訪問的所有必要步驟。
1.1驅(qū)動實現(xiàn)
添加創(chuàng)建gpio控制驅(qū)動bspkernelkernel4.14driversgpiogpio_led.c,并添加好對應(yīng)的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 ==============================// // 驅(qū)動模塊名稱定義 #define MODULE_NAME "gpio_led" // 驅(qū)動模塊的名字 #define MISC_NAME "gpio_led_device" // 用于注冊為“misc”設(shè)備的名字 // 模塊函數(shù)接口定義,供上層應(yīng)用調(diào)用的接口。通過MM_DEV_MAGIC區(qū)分不同系統(tǒng)接口,通過_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 描述數(shù)組 struct gpio_desc *led_gpio[SIMPIE_LED_MAX]; // cat命令將調(diào)用該函數(shù) 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命令將調(diào)用該函數(shù) 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的設(shè)備屬性 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設(shè)備 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; } // 驅(qū)動入口 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; } // 綁定設(shè)備 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); // 創(chuàng)建gpio_led_class設(shè)備 cls = class_create(THIS_MODULE, "gpio_led_class"); // 在gpio_led_class設(shè)備目錄下創(chuàng)建一個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"); 設(shè)備樹配置 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驅(qū)動后重新編譯更新kernel 可以在/dev目錄下找到對應(yīng)的設(shè)備文件
"/dev/gpio_led_device",通過讀寫設(shè)備文件就可以操作gpio了。
1.2創(chuàng)建 Native 庫
1.2.1設(shè)置 JNI 方法
在 App 中定義 JNI 方法以實現(xiàn)與 GPIO 設(shè)備的交互。
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實現(xiàn) Native 方法
在app/src/main目錄下創(chuàng)建一個cpp文件夾(如果你的項目是用Kotlin編寫的,這個步驟仍然適用,因為JNI是用C/C++實現(xiàn)的)。將你的libjni_gpiocontrol.cpp文件放到這個cpp目錄中。
注意事項:確保本地方法簽名正確,Java方法簽名和本地(C/C++)方法實現(xiàn)之間必須完全匹配。
#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調(diào)用 Native 方法
通過 JNI 接口在 App 中調(diào)用實現(xiàn)的 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); // 根據(jù)result處理結(jié)果 } }
2.SELinux 配置
由于直接訪問硬件設(shè)備在 Android 中受到 SELinux 策略的限制,需要修改 SELinux 策略以允許 App 訪問 GPIO 設(shè)備文件。
定義設(shè)備類型:為 GPIO 設(shè)備定義一個新的 SELinux 類型(如 gpio_led_device_t)。
在SDK_dir/device/sprd/sharkle/common/sepolicy/device.te 添加
# 定義新的設(shè)備類型 type gpio_led_device_t, dev_type;
分配文件上下文:為 GPIO 設(shè)備文件分配新定義的 SELinux 類型。
SDK_dir/device/sprd/sharkle/common/sepolicy/file_contexts中添加
/dev/gpio_led_device ugpio_led_device_t:s0
授予權(quán)限:在 SELinux 策略中添加規(guī)則,允許 App 訪問 GPIO 設(shè)備。
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 策略進行編譯,并將其部署到設(shè)備上。這一步驟的目的是將自定義的安全策略更改應(yīng)用到Android構(gòu)建系統(tǒng)的預(yù)設(shè)SELinux策略中,確保在編譯系統(tǒng)鏡像時,這些更改會被包含進去。
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 設(shè)備上測試 App。確保 App 能成功加載 native 庫,并能通過 JNI 調(diào)用控制 GPIO。
SELinux 策略測試:驗證 SELinux 策略更改是否允許 App 無障礙地訪問 GPIO 設(shè)備。
問題排查:如果遇到訪問被拒絕的情況,請檢查 SELinux 審計日志以確定是否需要進一步調(diào)整策略。
3.1注意事項
安全性:在修改 SELinux 策略以增加訪問權(quán)限時,務(wù)必小心謹(jǐn)慎,避免引入安全漏洞。
設(shè)備兼容性:確保你的實現(xiàn)考慮到了不同設(shè)備可能存在的硬件和配置差異。
文檔和維護:適當(dāng)記錄你的設(shè)計和實現(xiàn)過程,包括 JNI 接口、native 代碼和 SELinux 策略更改,以便于未來的審計和維護。
通過遵循以上步驟,你可以在遵守 Android 安全模型的同時,實現(xiàn) App 對 GPIO 的有效控制。
-
APP
+關(guān)注
關(guān)注
33文章
1578瀏覽量
72816 -
編譯
+關(guān)注
關(guān)注
0文章
661瀏覽量
33059 -
GPIO
+關(guān)注
關(guān)注
16文章
1217瀏覽量
52410
原文標(biāo)題:如何以JNI方式實現(xiàn)安卓APP控制GPIO?
文章出處:【微信號:萬象奧科,微信公眾號:萬象奧科】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
在Android移動設(shè)備上應(yīng)用機器學(xué)習(xí)
CC2650讀取Android app上顯示的服務(wù)問題
如何使用核心模塊開發(fā)設(shè)備控制功能(iOS APP開發(fā))
牛掰了!鴻蒙與Android完美融合,將鴻蒙設(shè)備當(dāng)Android設(shè)備用
如何通過APP控制串口屏和GPIO口的電平
如何通過kernel設(shè)備文件節(jié)點打通android app與kernel間的通信?
如何通過kernel設(shè)備文件節(jié)點打通android app與kernel間的通信?
請問如何在Android 9以上的設(shè)備上正常使用這個功能?
Android設(shè)備通過藍牙模塊實現(xiàn)無線終端的設(shè)計
![<b class='flag-5'>Android</b><b class='flag-5'>設(shè)備</b><b class='flag-5'>通過</b>藍牙模塊實現(xiàn)無線終端的設(shè)計](https://file.elecfans.com/web1/M00/61/E2/pIYBAFuGap6AC_-8AABe3JbxE2M718.jpg)
如何在嵌入式Linux中使用GPIO
未來Windows 10將會原生運行Android App?
Android: 使用Linux regulator系統(tǒng)通過一個GPIO控制外部IC的電源
![<b class='flag-5'>Android</b>: 使用Linux regulator系統(tǒng)<b class='flag-5'>通過</b>一個<b class='flag-5'>GPIO</b><b class='flag-5'>控制</b>外部IC的電源](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
如何在STM8S控制器上執(zhí)行通用GPIO功能實現(xiàn)LED閃爍
![如<b class='flag-5'>何在</b>STM8S<b class='flag-5'>控制</b>器<b class='flag-5'>上</b>執(zhí)行通用<b class='flag-5'>GPIO</b>功能實現(xiàn)LED閃爍](https://file.elecfans.com/web2/M00/5D/FE/poYBAGL0xaOAYCYrAAEkiIE0PzE805.png)
Arduino/Android藍牙伺服電機控制安卓APP
![Arduino/<b class='flag-5'>Android</b>藍牙伺服電機<b class='flag-5'>控制</b>安卓<b class='flag-5'>APP</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
通過WiFi使用Android設(shè)備控制機器人汽車
![<b class='flag-5'>通過</b>WiFi使用<b class='flag-5'>Android</b><b class='flag-5'>設(shè)備</b><b class='flag-5'>控制</b>機器人汽車](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
評論