AndroidQ 从app到驱动 第一章 编写Linux内核驱动程序

一直以来都想从驱动一直到应用,添加一套新的调用流程,来进一步理解Android系统的架构流程。

后面会连续写几篇文章,从Android系统的驱动一直到应用层一步一步添加接口,从驱动到应用。

由于目前手头仅有Android9.0的全套代码,因此就直接在Android9.0上面适配了,手头仅有Android 10的模拟器代码,因此没有在Android10上面适配。

AndroidQ 从app到驱动 第一章 编写Linux内核驱动程序

AndroidQ 从app到驱动 第二章 添加HAL层,先打通JNI层到驱动的访问

AndroidQ 从app到驱动 第三章 SystemServer服务中添加 HelloService

AndroidQ 从app到驱动 第四章 编写app验证新添加的helloservice是否正常

AndroidQ 从app到驱动 第五章 编写JNI层完成HelloService与Hal层的对接

AndroidQ 从app到驱动 第六章 从app到驱动的所有的代码整理

这篇文章主要介绍如何编写linux内核驱动。

这里参考了其他两个大牛的作品。

老罗的在Ubuntu上为Android系统编写Linux内核驱动程序

阳光玻璃杯的Android应用程序访问linux驱动第一步:实现并测试Linux驱动

驱动部分的代码是复制的阳光玻璃杯的代码,我这边基本上没有进行任何修改。实现的功能就是提供一个字符串缓冲区,

然后应用通过层层调用读写这个字符串。

我们的linux内核是 4.4的平台是mtk的6739平台

首先我们看下添加驱动具体添加了哪些文件,修改了哪些文件

上面的截图是我这边通过git status 看到的具体修改了哪些文件。

从截图中可以看到,修改了kernel-4.4/drivers/Kconfig 以及 kernel-4.4/drivers/Makefile 

同时在kernel-4.4/drivers/目录下面添加了一个 hello目录

下面我们分别看下 kernel-4.4/drivers/Kconfig 以及 kernel-4.4/drivers/Makefile  的修改

从上面两个截图可以看到,这两个文件的修改非常简单,

其中 kernel-4.4/drivers/Kconfig 只添加了一行  source "drivers/hello/Kconfig"

 kernel-4.4/drivers/Makefile 中添加了一行  obj-y                   += hello/

这里前面的文章都提到在kernel-4.4/drivers/Makefile 中添加 obj-$(CONFIG_HELLO) += hello/ 然后通过make menuconfig来进行配置,但是我这对make menuconfig不太熟,因此就直接写成了 obj-y    += hello/ 这样子就不需要进行配置了,新编写的驱动可以直接编译进内核。

下面我们看下 hell目录下面的文件列表

hello目录下面就只有四个文件,如图所示。

下面就四个文件的内容,粘贴出来

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"

struct hello_android_dev {
    char * val;
    struct semaphore sem;
    struct cdev dev;
};

#endif

hello.c 关于这个文件中的函数注解,大家可以查看 阳光玻璃杯的Android应用程序访问linux驱动第一步:实现并测试Linux驱动

#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;  
    }  

    printk(KERN_ALERT"hello_read AAAAAAAAAAAAAA.\n");

    if(count < sizeof(dev->val)) {  
        goto out;  
    }          

    printk(KERN_ALERT"hello_read BBBBBBBBBBBBBBB.\n");

    if(copy_to_user(buf, dev->val, sizeof(dev->val))) {  
        err = -EFAULT;  
        goto out;  
    }  

    printk(KERN_ALERT"hello_read CCCCCCCCCCCCCCCCC.\n");

    err = sizeof(dev->val);  

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;          
    }          

    printk(KERN_ALERT"hello_write AAAAAAAAAAAAAA.\n");

    if(count != sizeof(dev->val)) {  
        goto out;          
    }          

    printk(KERN_ALERT"hello_write BBBBBBBBBBBBBBB.\n");

    if(copy_from_user(dev->val, buf, count)) {  
        err = -EFAULT;  
        goto out;  
    }  

    printk(KERN_ALERT"hello_write CCCCCCCCCCCCCC.\n");
    err = sizeof(dev->val);  

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,count);
    printk(KERN_ALERT"__hello_set_val.dev->val: %s  count:%d\n",dev->val,count);
    strncpy(dev->val,buf, count);
    printk(KERN_ALERT"__hello_set_val.dev->val: %s  count:%d\n",dev->val,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,count);
    return __hello_set_val(hdev, buf, count);  
}  

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(100,GFP_KERNEL);
    // Dev的默认值是 hello_device	
    strncpy(dev->val,"hello_device",sizeof("hello_device"));
    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);

Kconfig

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

Makefile

obj-y +=hello.o

上面就是hello目录下面四个文件的具体内容了。

上面的文件添加完毕之后,就可以编译kernel进行验证了

首先编译 kernel 

make clean-kernel && make kernel -j48

然后编译bootimg

make bootimage

然后将编译出来的 boot.img 烧录进手机进行验证。

boot.img编译成功。然后单独烧录boot.img到手机。重启手机,进行验证。

验证结果如上图,驱动添加成功。

  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值