一、开发环境
- 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
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> #define LED_ON 1 #define LED_OFF 0 static int led_fd = -1; static int led_state = LED_OFF; static ssize_t myled_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { printk(KERN_ALERT"############# myled_write ########\n"); char led_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的操作 ... } else if (led_on == LED_OFF && led_state != LED_OFF) { // 确保LED硬件处于打开状态再关闭 // ... 执行关闭LED的操作 ... } else { // 如果请求的LED状态与当前状态相同,则无需操作 return 0; } led_state = led_on ? LED_ON : LED_OFF; return 1; } static ssize_t myled_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { printk(KERN_ALERT"############# myled_read ########\n"); // 实现读逻辑 return 0; } static const struct file_operations myled_fops = { .owner = THIS_MODULE, .read = myled_read, .write = myled_write, }; static struct miscdevice myled_misc = { .minor = MISC_DYNAMIC_MINOR, .name = "myled", .fops = &myled_fops, .mode = 0666, // 根据安全需求调整,这里设置无效,需要单独配置 SELinux 和 UAC 权限 }; static int __init led_init(void) { // 用 misc_register() 而非 register_chrdev(),因为前者会自动处理设备号分配并触发热插拔事件,简化设备节点的创建 // led_fd = register_chrdev(0, "myleddev", &myled_fops); // return 0; return misc_register(&myled_misc); } static void __exit led_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
cconfig MY_LED bool "My Led Test" default y help This is a led demo to test kernel experiment On Android . -
创建 Makefile 文件 YourSource/kernel/xiaomi/mt6768/drivers/char/myled/Makefile
c# obj-m 会编译成 myled.o 文件, 这里使用 obj-y 直接编译到 boot.img 中 obj-y += myled.o
三、将驱动配置到 kernelconfig 中,配置好后才能编译到 boot.img 中
-
在 YourSource/kernel/xiaomi/mt6768/drivers/char/Kconfig 中添加以下内容:
bashsource "drivers/char/myled/Kconfig" -
在 YourSource/kernel/xiaomi/mt6768/drivers/char/Makefile 中添加以下内容:
bashobj-y += myled/
四、配置 SELinux 权限
-
在 YourSource/device/xiaomi/mt6768-common/sepolicy/vendor/file.te 中添加以下内容:
c# myled,为led驱动定义一个设备类型,以方便SELinux权限管理 type myled_device, dev_type; -
在 YourSource/device/xiaomi/mt6768-common/sepolicy/vendor/file_contexts 中添加以下内容:
c# myled /dev/myled u:object_r:myled_device:s0因为 myled 挂载在 /dev 下面,所以在 file_contexts 中添加上面的权限即可,如果是在 /sys 下面,需要使用 genfscon sysfs 来添加权限(请自行搜索 genfscon sysfs、genfscon debugfs 和 genfscon devtmpfs 的使用方法)。
-
在 YourSource/device/xiaomi/mt6768-common/sepolicy/vendor/system_app.te 中添加以下内容:
c# myled allow system_app myled_device:chr_file { getattr open read write ioctl };
五、配置 UAC 权限
-
在 YourSource/device/xiaomi/mt6768-common/rootdir/etc/ueventd.mtk.rc 中添加以下内容:
bash/dev/myled 0660 system system
六、编译并刷入镜像
- 驱动在 boot 分区,需要编译 boot.img 文件
bash
# 编译 boot.img 文件
mka bootimage
# 进入 fastboot 模式
adb reboot bootloader
# 刷入 boot.img 文件
fastboot flash boot boot.img
- SELinux 配置在 vendor 分区,需要编译 vendor.img 文件
bash
# 编译 vendor.img 文件
mka vendorimage
# android 10 以上需要进入 fastbootd 模式
adb reboot fastboot
# 刷入 vendor.img 文件
fastboot flash vendor vendor.img
七、测试驱动
验证驱动是否刷入成功,执行下面的命令后,显示如下则说明正确配置并刷入了。
shell
(base) test@test:~$ adb shell ls -l /dev/myled
crw-rw---- 1 system system 10, 61 2026-09-28 10:55 /dev/myled
(base) test@test:~$ adb shell ls -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 模式