这个模块共有四篇:
1.实现 Android 字符设备驱动
2.Android 15 及以上版本创建系统级 app 并测试驱动
3.在 Android 系统中自定义 LedManagerService 服务并提供给 app 使用
4.为 LedManagerService 添加区权限申请功能
一、开发环境
- LineageOS 23.2 (Android 16)
- Ubuntu 24.04.4 LTS
- 红米9 (lancelot)已解锁 BL 并刷如 LineageOS 23.2
二、实现字符设备驱动
这里以模拟LED为例,实现一个字符设备驱动,用于控制LED的亮灭。
首先,在 YourSource/kernel/xiaomi/mt6768/drivers/char 目录下创建一个目录 myled,用于存放字符设备驱动的源文件。
创建驱动文件 YourSource/kernel/xiaomi/mt6768/drivers/char/myled/myled.c
#include<linux/module.h>#include<linux/kernel.h>#include<linux/init.h>#include<linux/fs.h>#include<linux/cdev.h>#include<linux/uaccess.h>#include<linux/miscdevice.h>#defineLED_ON1#defineLED_OFF0staticintled_fd=-1;staticintled_state=LED_OFF;staticssize_tmyled_write(structfile*filp,constchar__user*buf,size_tcount,loff_t*f_pos){printk(KERN_ALERT"############# myled_write ########\n");charled_on;if(count!=1)return-EINVAL;if(copy_from_user(&led_on,buf,1))return-EFAULT;if(led_on==LED_ON&&led_state!=LED_ON){// 确保LED硬件处于关闭状态再打开// ... 执行打开LED的操作 ...}elseif(led_on==LED_OFF&&led_state!=LED_OFF){// 确保LED硬件处于打开状态再关闭// ... 执行关闭LED的操作 ...}else{// 如果请求的LED状态与当前状态相同,则无需操作return0;}led_state=led_on?LED_ON:LED_OFF;return1;}staticssize_tmyled_read(structfile*file,char__user*buf,size_tcount,loff_t*ppos){printk(KERN_ALERT"############# myled_read ########\n");// 实现读逻辑return0;}staticconststructfile_operationsmyled_fops={.owner=THIS_MODULE,.read=myled_read,.write=myled_write,};staticstructmiscdevicemyled_misc={.minor=MISC_DYNAMIC_MINOR,.name="myled",.fops=&myled_fops,.mode=0666,// 根据安全需求调整,这里设置无效,需要单独配置 SELinux 和 UAC 权限};staticint__initled_init(void){// 用 misc_register() 而非 register_chrdev(),因为前者会自动处理设备号分配并触发热插拔事件,简化设备节点的创建// led_fd = register_chrdev(0, "myleddev", &myled_fops);// return 0;returnmisc_register(&myled_misc);}staticvoid__exitled_exit(void){// unregister_chrdev(led_fd, "myleddev");misc_deregister(&myled_misc);}module_init(led_init);module_exit(led_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("LED");MODULE_DESCRIPTION("A simple LED driver for Android.");创建配置文件 YourSource/kernel/xiaomi/mt6768/drivers/char/myled/Kconfig
config MY_LED bool"My Led Test"defaulty help This is a led demo to test kernel experiment On Android.创建 Makefile 文件 YourSource/kernel/xiaomi/mt6768/drivers/char/myled/Makefile
#obj-m 会编译成 myled.o 文件,这里使用 obj-y 直接编译到 boot.img 中obj-y+=myled.o
三、将驱动配置到 kernelconfig 中,配置好后才能编译到 boot.img 中
- 在 YourSource/kernel/xiaomi/mt6768/drivers/char/Kconfig 中添加以下内容:
source"drivers/char/myled/Kconfig" - 在 YourSource/kernel/xiaomi/mt6768/drivers/char/Makefile 中添加以下内容:
obj-y+=myled/
四、配置 SELinux 权限
- 在 YourSource/device/xiaomi/mt6768-common/sepolicy/vendor/file.te 中添加以下内容:
#myled,为led驱动定义一个设备类型,以方便SELinux权限管理type myled_device,dev_type; - 在 YourSource/device/xiaomi/mt6768-common/sepolicy/vendor/file_contexts 中添加以下内容:
因为 myled 挂载在 /dev 下面,所以在 file_contexts 中添加上面的权限即可,如果是在 /sys 下面,需要使用 genfscon sysfs 来添加权限(请自行搜索 genfscon sysfs、genfscon debugfs 和 genfscon devtmpfs 的使用方法)。#myled/dev/myled u:object_r:myled_device:s0 - 在 YourSource/device/xiaomi/mt6768-common/sepolicy/vendor/system_app.te 中添加以下内容:
#myledallow system_app myled_device:chr_file{getattr open read write ioctl};
五、配置 UAC 权限
- 在 YourSource/device/xiaomi/mt6768-common/rootdir/etc/ueventd.mtk.rc 中添加以下内容:
/dev/myled 0660 system system
六、编译并刷入镜像
- 驱动在 boot 分区,需要编译 boot.img 文件
# 编译 boot.img 文件mka bootimage# 进入 fastboot 模式adbrebootbootloader# 刷入 boot.img 文件fastboot flash boot boot.img- SELinux 配置在 vendor 分区,需要编译 vendor.img 文件
# 编译 vendor.img 文件mka vendorimage# android 10 以上需要进入 fastbootd 模式adbrebootfastboot# 刷入 vendor.img 文件fastboot flash vendor vendor.img七、测试驱动
验证驱动是否刷入成功,执行下面的命令后,显示如下则说明正确配置并刷入了。
(base)test@test:~$ adb shellls-l/dev/myled crw-rw----1system system10,612026-09-2810:55 /dev/myled(base)test@test:~$ adb shellls-Z/dev/myled u:object_r:myled_device:s0 /dev/myled因为 LED 驱动在 system 域,所以需要创建个系统级 app 来测试驱动。由于 Android 15 及以上版本,在 app 中添加 sharedUserId=“android.uid.system” 并不能实现系统级权限,所以还是在下一篇中介绍怎么处理吧。
八、总结
- 驱动在 boot 分区,需要编译 boot.img 文件
- SELinux 配置在 vendor 分区,需要编译 vendor.img 文件,而 Android 10 以上需要进入 fastbootd 模式