实现 Android 字符设备驱动

一、开发环境

  • LineageOS 23.2 (Android 16)
  • Ubuntu 24.04.4 LTS
  • 红米9 (lancelot)已解锁 BL 并刷如 LineageOS 23.2

二、实现字符设备驱动

这里以模拟LED为例,实现一个字符设备驱动,用于控制LED的亮灭。

  1. 首先,在 YourSource/kernel/xiaomi/mt6768/drivers/char 目录下创建一个目录 myled,用于存放字符设备驱动的源文件。

  2. 创建驱动文件 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.");
  3. 创建配置文件 YourSource/kernel/xiaomi/mt6768/drivers/char/myled/Kconfig

    c 复制代码
    config MY_LED
        bool "My Led Test"
        default y
        help
        This is a led demo to test kernel experiment On Android .
  4. 创建 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 中

  1. 在 YourSource/kernel/xiaomi/mt6768/drivers/char/Kconfig 中添加以下内容:

    bash 复制代码
    source "drivers/char/myled/Kconfig"
  2. 在 YourSource/kernel/xiaomi/mt6768/drivers/char/Makefile 中添加以下内容:

    bash 复制代码
    obj-y				+= myled/

四、配置 SELinux 权限

  1. 在 YourSource/device/xiaomi/mt6768-common/sepolicy/vendor/file.te 中添加以下内容:

    c 复制代码
    # myled,为led驱动定义一个设备类型,以方便SELinux权限管理
    type myled_device, dev_type;
  2. 在 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 的使用方法)。

  3. 在 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 权限

  1. 在 YourSource/device/xiaomi/mt6768-common/rootdir/etc/ueventd.mtk.rc 中添加以下内容:

    bash 复制代码
    /dev/myled           0660   system     system

六、编译并刷入镜像

  1. 驱动在 boot 分区,需要编译 boot.img 文件
bash 复制代码
# 编译 boot.img 文件
mka bootimage
# 进入 fastboot 模式
adb reboot bootloader
# 刷入 boot.img 文件
fastboot flash boot boot.img
  1. 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" 并不能实现系统级权限,所以还是在下一篇中介绍怎么处理吧。

八、总结

  1. 驱动在 boot 分区,需要编译 boot.img 文件
  2. SELinux 配置在 vendor 分区,需要编译 vendor.img 文件,而 Android 10 以上需要进入 fastbootd 模式
相关推荐
额额额对了1 小时前
嵌入式 ADC 从入门到实战:基于 i.MX6ULL 的裸机驱动开发
驱动开发·嵌入式硬件·arm
sukalot1 小时前
Windows 驱动实例分析系列:libwdi 驱动分析 - examples 篇(四)
windows·驱动开发
传奇开心果编程1 小时前
【Flutter入门练中学】第3课:滚动与列表
android·学习·flutter·ui·ios
JosieBook1 小时前
【数据库】MySQL 实战精通系列 · 第4篇:索引原理与执行计划实战
android·数据库·mysql
mmsx1 小时前
Android UI 复用体系:泛型基类、骨架与插槽
android·kotlin
Cici_ovo2 小时前
手机数据删除原理、运行内存、闪存、机械硬盘完整原理详解
android·智能手机
hi_LeTian2 小时前
【Linphone】Ubuntu20.04 编译 Linphone5.2 Android SDK 适配RK3326硬解码完整教程(避坑版)
android
SoStraw2 小时前
跨平台P2P通信SDK,Windows/Android/iOS/Linux/macOS一套API全兼容
android·linux·windows·跨平台·p2p·webrpc
李游Leo2 小时前
《HarmonyOS 7 ArkGraphics 3D 空间设计开发实战》07:复杂3D场景的帧率、内存与资源性能优化【鸿蒙心迹】
android·3d·性能优化·harmonyos