3. 编写linux内核驱动程序(Android 10)

本文是在上文2. 编译安卓内核(pixel 2,Android 10)的基础上进行的,虚拟机环境就是上文结束时的环境。

1)首先安装vscode,方便编辑代码。

linux驱动程序简介

2)

在Linux系统中,一切皆文件。
所以在Linux中,设备也被作为一种文件来操作。而实现这些操作的,就叫做设备驱动。
在Linux中,设备被分为三类:
    - 字符设备(如,鼠标,键盘==)
    - 块设备(如硬盘)
    - 网络设备(这里指网络接口,如常见的eth0,wlan0,lo)

我们都知道,Linux一切皆文件,且,Linux具有两个空间——用户与内核.那么具体到用户态的表现就是,我们Open某个设备后,我们就会得到一个在系统中唯一的文件描述符号fd。我们所有关于设备的操作,都是以fd为依据进行操作。
本文使用一个虚拟的设备,即一个字符串指针(即struct hello_android_dev中的val,可读可写)作为示例,在安卓内核中增加一个名为hello驱动程序来操作这个虚拟设备(open、release、read、write)。

增加hello驱动

3)

在drivers目录下新建hello文件夹 

在hello文件夹里新建四个文件,文件具体内容如下。

 Kconfig

config HELLO
 tristate "Hello Android Driver"
 default n
 help
 This is the hello android driver.

Makefile

obj-y +=hello.o

hello.h

#ifndef _HELLO_ANDROID_H_
#define _HELLO_ANDROID_H_ 
 
#include <linux/cdev.h>
#include <linux/semaphore.h>
 
#define HELLO_DEVICE_NODE_NAME  "hello"
#define HELLO_DEVICE_FILE_NAME  "hello"
#define HELLO_DEVICE_CLASS_NAME "hello"
#define VAL_LENGTH 100
 
struct hello_android_dev {
    char *val;
    struct semaphore sem;
    struct cdev dev;
};
 
#endif

 hello.c

#include <linux/init.h>  
#include <linux/module.h>  
#include <linux/types.h>  
#include <linux/fs.h>  
#include <linux/proc_fs.h>  
#include <linux/device.h>  
 
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
 
#include <linux/poll.h>
#include <linux/seq_file.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
 
#include <asm/uaccess.h>
#include <linux/slab.h>
 
 
#include "hello.h"  
 
static int hello_major = 0;  
static int hello_minor = 0;  
 
static struct class* hello_class = NULL;  
static struct hello_android_dev* hello_dev = NULL;  
 
 
// 这四个函数供hal层调用
// 分别对应hal层打开,关闭,写入,读取操作
 
static int hello_open(struct inode* inode, struct file* filp);  
static int hello_release(struct inode* inode, struct file* filp);  
static ssize_t hello_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos);  
static ssize_t hello_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos);  
 
static struct file_operations hello_fops = {  
    .owner = THIS_MODULE,  
    .open = hello_open,  
    .release = hello_release,  
    .read = hello_read,  
    .write = hello_write,   
};  
 
 
// 这两个函数用于处理 DEVICE_ATTR 这个宏定义的处理
// 这个宏主要是在 /sys/devices/virtual/ 目录下生成对应的文件,使得开发人员可以通过 cat和echo 来进行操作
// 可以参考 https://www.cnblogs.com/lifexy/p/9799778.html 了解详情
static ssize_t hello_val_show(struct device* dev, struct device_attribute* attr,  char* buf);  
static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count);  
 
static DEVICE_ATTR(val, S_IRUGO|S_IWUSR, hello_val_show, hello_val_store); 
 
static int hello_open(struct inode* inode, struct file* filp) {  
    struct hello_android_dev* dev;          
    printk(KERN_ALERT"hello_open.\n");
    dev = container_of(inode->i_cdev, struct hello_android_dev, dev);  
    filp->private_data = dev;  
    return 0;  
}  
 
 
static int hello_release(struct inode* inode, struct file* filp) {  
    return 0;  
}
 
 
static ssize_t hello_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) {
 
    ssize_t err = 0;
    struct hello_android_dev* dev = filp->private_data;
    printk(KERN_ALERT"hello_read.\n");
 
    if(down_interruptible(&(dev->sem))) {
        return -ERESTARTSYS;
    }
 
    if(count >= VAL_LENGTH) {
        goto out;
    }
 
    printk(KERN_ALERT"hello_read %s.\n", dev->val);
    printk(KERN_ALERT"hello_read %d.\n", (int)strlen(dev->val)+1);
 
    if(copy_to_user(buf, dev->val, count)) {
        err = -EFAULT;
        goto out;
    }
 
    err = count;
 
out:
    up(&(dev->sem));
    return err;
}  
 
static ssize_t hello_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) {
 
    struct hello_android_dev* dev = filp->private_data;
    ssize_t err = 0;
 
    if(down_interruptible(&(dev->sem))) {
        return -ERESTARTSYS;
    }
 
    if(count >= VAL_LENGTH) {
        goto out;
    }
 
    if(copy_from_user(dev->val, buf, count)) {
        err = -EFAULT;
        goto out;
    }
    err = count;
 
out:  
    up(&(dev->sem));  
    return err;
}
 
 
static ssize_t __hello_set_val(struct hello_android_dev* dev, const char* buf, size_t count) {       
    printk(KERN_ALERT"__hello_set_val.\n"); 
    if(down_interruptible(&(dev->sem))) {                  
        return -ERESTARTSYS;          
    }          
    printk(KERN_ALERT"__hello_set_val.buf: %s  count:%d\n",buf,(int)count);
    printk(KERN_ALERT"__hello_set_val.dev->val: %s  count:%d\n",dev->val,(int)count);
    strncpy(dev->val,buf, count);
    printk(KERN_ALERT"__hello_set_val.dev->val: %s  count:%d\n",dev->val,(int)count);
    up(&(dev->sem));  
 
    return count;  
}  
 
static ssize_t hello_val_show(struct device* dev, struct device_attribute* attr, char* buf) {
    struct hello_android_dev* hdev = (struct hello_android_dev*)dev_get_drvdata(dev);          
    printk(KERN_ALERT"hello_val_show.\n");
    printk(KERN_ALERT"%s\n",hdev->val);
    return sprintf(buf,"%s\n",hdev->val);
}  
 
static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count) {  
    struct hello_android_dev* hdev = (struct hello_android_dev*)dev_get_drvdata(dev);    
    printk(KERN_ALERT"hello_val_store.buf: %s  count:%d\n",buf,(int)count);
    return __hello_set_val(hdev, buf, count+1);
}  
 
static int  __hello_setup_dev(struct hello_android_dev* dev) {  
 
    int err;  
    dev_t devno = MKDEV(hello_major, hello_minor);  
    printk(KERN_ALERT"__hello_setup_dev.\n");
    memset(dev, 0, sizeof(struct hello_android_dev));  
 
    cdev_init(&(dev->dev), &hello_fops);  
    dev->dev.owner = THIS_MODULE;  
    dev->dev.ops = &hello_fops;          
 
    err = cdev_add(&(dev->dev),devno, 1);  
    if(err) {  
        return err;  
    }          
 
	sema_init(&(dev->sem), 1);
	// 给val变量开辟空间,这里只有100个字节,如果设置的字符串长度超过,后面的会被丢弃
    dev->val = kmalloc(VAL_LENGTH,GFP_KERNEL);
    // Dev的默认值是 hello_device
    strncpy(dev->val,"hello_device",strlen("hello_device")+1);
    return 0;  
}  
 
// 驱动初始化函数
static int __init hello_init(void){   
    int err = -1;  
    dev_t dev = 0;  
    struct device* temp = NULL;  
 
    printk(KERN_ALERT"hello_init.\n");
 
    err = alloc_chrdev_region(&dev, 0, 1, HELLO_DEVICE_NODE_NAME);  
    if(err < 0) {  
        printk(KERN_ALERT"Failed to alloc char dev region.\n");  
        goto fail;  
    }  
 
    hello_major = MAJOR(dev);  
    hello_minor = MINOR(dev);          
 
    hello_dev = kmalloc(sizeof(struct hello_android_dev), GFP_KERNEL);  
    if(!hello_dev) {  
        err = -ENOMEM;  
        printk(KERN_ALERT"Failed to alloc hello_dev.\n");  
        goto unregister;  
    }          
 
    err = __hello_setup_dev(hello_dev);  
    if(err) {  
        printk(KERN_ALERT"Failed to setup dev: %d.\n", err);  
        goto cleanup;  
    }          
 
    hello_class = class_create(THIS_MODULE, HELLO_DEVICE_CLASS_NAME);  
    if(IS_ERR(hello_class)) {  
        err = PTR_ERR(hello_class);  
        printk(KERN_ALERT"Failed to create hello class.\n");  
        goto destroy_cdev;  
    }          
 
    temp = device_create(hello_class, NULL, dev, "%s", HELLO_DEVICE_FILE_NAME);  
    if(IS_ERR(temp)) {  
        err = PTR_ERR(temp);  
        printk(KERN_ALERT"Failed to create hello device.");  
        goto destroy_class;  
    }          
 
    err = device_create_file(temp, &dev_attr_val);  
    if(err < 0) {  
        printk(KERN_ALERT"Failed to create attribute val.");                  
        goto destroy_device;  
    }  
 
    dev_set_drvdata(temp, hello_dev);          
 
    printk(KERN_ALERT"Succedded to initialize hello device.\n");  
    return 0;  
 
destroy_device:  
    device_destroy(hello_class, dev);  
 
destroy_class:  
    class_destroy(hello_class);  
 
destroy_cdev:  
    cdev_del(&(hello_dev->dev));  
 
cleanup:  
    kfree(hello_dev);  
 
unregister:  
    unregister_chrdev_region(MKDEV(hello_major, hello_minor), 1);  
 
fail:  
    return err;  
}  
 
// 驱动卸载函数
static void __exit hello_exit(void) {  
    dev_t devno = MKDEV(hello_major, hello_minor);  
 
    printk(KERN_ALERT"hello_exit\n");          
 
    if(hello_class) {  
        device_destroy(hello_class, MKDEV(hello_major, hello_minor));  
        class_destroy(hello_class);  
    }          
 
    if(hello_dev) {  
        cdev_del(&(hello_dev->dev));  
        kfree(hello_dev);  
    }          
    if(hello_dev->val != NULL){
     kfree(hello_dev->val);
    }
    unregister_chrdev_region(devno, 1);  
}  
 
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Hello Driver");
 
module_init(hello_init);
module_exit(hello_exit);

修改drivers/Kconfig,加一行

source "drivers/hello/Kconfig"

修改drivers/Makefile,加一行

obj-y                   += hello/

 

编译

4)编译

cd ~/Documents/msm
export PATH=$PATH:/home/test/Documents/aosp10/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin
export PATH=$PATH:/home/test/Documents/aosp10/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin
export PATH=$PATH:/home/test/Documents/aosp10/prebuilts/misc/linux-x86/dtc
export PATH=$PATH:/home/test/Documents/aosp10/prebuilts/misc/linux-x86/libufdt
export PATH=$PATH:/home/test/Documents/aosp10/prebuilts/misc/linux-x86/lz4
export ARCH=arm64
export CROSS_COMPILE=/home/test/Documents/aosp10/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin/aarch64-linux-android-
export CROSS_COMPILE_ARM32=/home/test/Documents/aosp10/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin/arm-linux-androideabi-
export CC=/home/test/Documents/aosp10/prebuilts/clang/host/linux-x86/clang-3289846/bin/clang
make wahoo_defconfig
make clean
make -j16

 编译成功

打包并刷入手机 

5)打包,新开一个terminal

cd ~/Documents/aosp10
export TARGET_PREBUILT_KERNEL=/home/test/Documents/msm/arch/arm64/boot/Image.lz4-dtb
source build/envsetup.sh
lunch
aosp_walleye-userdebug
time make bootimage

6)刷入手机

新开一个terminal

cd ~/Documents/aosp10
sudo su
source build/envsetup.sh
lunch
aosp_walleye-userdebug
adb reboot bootloader
cd out/target/product/walleye/
fastboot flash boot boot.img
fastboot reboot

测试

7)

adb shell
su
ls -l /dev/hello
ls -l /sys/devices/virtual/hello/hello/
cd /sys/devices/virtual/hello/hello/
cat val
echo "my_driver\0" > val
cat val

本文涉及到的知识主要和Android架构中的linux内核层相关

参考

AndroidQ 从app到驱动 第一章 编写Linux内核驱动程序_长乐居士-CSDN博客

在Ubuntu上为Android系统编写Linux内核驱动程序_老罗的Android之旅-CSDN博客

Linux Char-Driver (字符驱动 摘要)(一)_Sky的专栏-CSDN博客

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值